개요

Column은 자식 위젯들을 수직 방향으로 배치하는 레이아웃 위젯입니다.

Column은 Flex 위젯의 편의 래퍼로, 방향이 수직으로 고정되어 있습니다. 여러 위젯을 세로로 나란히 배치할 때 사용하며, 다양한 정렬 옵션을 제공합니다. 기본적으로 Column은 가능한 모든 수직 공간을 차지하지만, mainAxisSize를 조정하여 필요한 만큼만 차지하도록 설정할 수 있습니다.

언제 사용하나요?

  • 폼 필드를 세로로 나열할 때
  • 리스트 아이템의 내용을 구성할 때
  • 카드나 다이얼로그의 수직 레이아웃을 만들 때
  • 메뉴 항목을 세로로 배치할 때
  • 제목, 부제목, 본문을 순서대로 표시할 때

기본 사용법

// 간단한 예제
const column = Column({
  children: [
    Text('Title'),
    Text('Subtitle'),
    Text('Body')
  ]
});

// 정렬 옵션 사용
const alignedColumn = Column({
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Text('왼쪽 정렬된'),
    Text('세로 가운데'),
    Text('텍스트들')
  ]
});

Props

children (필수)

값: Widget[]

Column에 표시할 자식 위젯들의 배열입니다. 배열 순서대로 위에서 아래로 배치됩니다.

Column({
  children: [
    Container({ width: 100, height: 50, color: 'red' }),
    Container({ width: 100, height: 50, color: 'green' }),
    Container({ width: 100, height: 50, color: 'blue' })
  ]
})

mainAxisAlignment

값: MainAxisAlignment (기본값: MainAxisAlignment.start)

자식 위젯들의 수직 정렬 방식을 정의합니다.

  • start: 상단 정렬 (기본값)
  • end: 하단 정렬
  • center: 가운데 정렬
  • spaceBetween: 첫 번째와 마지막 위젯을 양 끝에 배치하고 나머지 공간을 균등 분배
  • spaceAround: 각 위젯 주변에 균등한 공간 분배
  • spaceEvenly: 모든 위젯과 가장자리 사이의 공간을 균등 분배
// 가운데 정렬
Column({
  mainAxisAlignment: MainAxisAlignment.center,
  children: [
    Text('A'),
    Text('B'),
    Text('C')
  ]
})

// 공간 균등 분배
Column({
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Icon({ icon: Icons.home }),
    Icon({ icon: Icons.search }),
    Icon({ icon: Icons.settings })
  ]
})

crossAxisAlignment

값: CrossAxisAlignment (기본값: CrossAxisAlignment.center)

자식 위젯들의 수평 정렬 방식을 정의합니다.

  • start: 왼쪽 정렬
  • end: 오른쪽 정렬
  • center: 가로 가운데 정렬 (기본값)
  • stretch: 사용 가능한 가로 공간을 모두 차지하도록 늘림
Column({
  crossAxisAlignment: CrossAxisAlignment.start,
  children: [
    Container({ width: 100, height: 50, color: 'red' }),
    Container({ width: 150, height: 50, color: 'green' }),
    Container({ width: 75, height: 50, color: 'blue' })
  ]
})

mainAxisSize

값: MainAxisSize (기본값: MainAxisSize.max)

Column이 차지하는 수직 공간의 크기를 제어합니다.

  • max: 사용 가능한 모든 수직 공간을 차지 (기본값)
  • min: 자식 위젯들에 필요한 최소 공간만 차지
// 필요한 공간만 차지
Container({
  color: 'lightgray',
  child: Column({
    mainAxisSize: MainAxisSize.min,
    children: [
      Text('Compact'),
      Text('Column')
    ]
  })
})

verticalDirection

값: VerticalDirection (기본값: VerticalDirection.down)

자식 위젯들의 수직 배치 순서를 정의합니다.

  • down: 위에서 아래로 (기본값)
  • up: 아래에서 위로
Column({
  verticalDirection: VerticalDirection.up,
  children: [
    Text('1번'), // 맨 아래 표시
    Text('2번'), // 중간 표시
    Text('3번')  // 맨 위 표시
  ]
})

고급 사용법

Expanded와 함께 사용하기

Column({
  children: [
    Container({ width: 100, height: 50, color: 'red' }),
    Expanded({
      child: Container({ width: 100, color: 'green' })
    }),
    Container({ width: 100, height: 50, color: 'blue' })
  ]
})
// 녹색 Container가 남은 수직 공간을 모두 차지합니다

비율로 공간 분배하기

Column({
  children: [
    Flexible({
      flex: 1,
      child: Container({ color: 'red' })
    }),
    Flexible({
      flex: 2,
      child: Container({ color: 'green' })
    }),
    Flexible({
      flex: 1,
      child: Container({ color: 'blue' })
    })
  ]
})
// 빨간색 25%, 녹색 50%, 파란색 25%의 높이를 차지합니다

실제 사용 예제

예제 1: 로그인 폼

const loginForm = Container({
  padding: EdgeInsets.all(20),
  child: Column({
    mainAxisAlignment: MainAxisAlignment.center,
    crossAxisAlignment: CrossAxisAlignment.stretch,
    children: [
      Text('로그인', {
        style: TextStyle({ fontSize: 24, fontWeight: 'bold' })
      }),
      SizedBox({ height: 30 }),
      TextField({
        decoration: InputDecoration({
          labelText: '이메일',
          border: OutlineInputBorder()
        })
      }),
      SizedBox({ height: 16 }),
      TextField({
        obscureText: true,
        decoration: InputDecoration({
          labelText: '비밀번호',
          border: OutlineInputBorder()
        })
      }),
      SizedBox({ height: 24 }),
      ElevatedButton({
        onPressed: () => console.log('로그인'),
        child: Text('로그인')
      }),
      SizedBox({ height: 16 }),
      TextButton({
        onPressed: () => console.log('회원가입'),
        child: Text('계정이 없으신가요? 회원가입')
      })
    ]
  })
});

예제 2: 카드 컨텐츠

const card = Container({
  margin: EdgeInsets.all(16),
  padding: EdgeInsets.all(16),
  decoration: BoxDecoration({
    color: 'white',
    borderRadius: BorderRadius.circular(12),
    boxShadow: [
      BoxShadow({
        color: 'rgba(0, 0, 0, 0.1)',
        blurRadius: 10,
        offset: { x: 0, y: 4 }
      })
    ]
  }),
  child: Column({
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Image({
        src: 'product.jpg',
        height: 200,
        fit: 'cover'
      }),
      SizedBox({ height: 16 }),
      Text('상품명', {
        style: TextStyle({ fontSize: 20, fontWeight: 'bold' })
      }),
      SizedBox({ height: 8 }),
      Text('상품 설명이 여기에 표시됩니다...', {
        style: TextStyle({ color: 'gray' })
      }),
      SizedBox({ height: 16 }),
      Row({
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text('₩25,000', {
            style: TextStyle({ fontSize: 18, fontWeight: 'bold', color: 'blue' })
          }),
          IconButton({
            icon: Icons.favorite_border,
            onPressed: () => {}
          })
        ]
      })
    ]
  })
});

예제 3: 설정 메뉴

const settingsMenu = Column({
  children: [
    ListTile({
      leading: Icon({ icon: Icons.person }),
      title: Text('프로필'),
      trailing: Icon({ icon: Icons.arrow_forward_ios }),
      onTap: () => console.log('프로필')
    }),
    Divider(),
    ListTile({
      leading: Icon({ icon: Icons.notifications }),
      title: Text('알림'),
      trailing: Switch({
        value: true,
        onChanged: (value) => {}
      })
    }),
    Divider(),
    ListTile({
      leading: Icon({ icon: Icons.lock }),
      title: Text('개인정보 보호'),
      trailing: Icon({ icon: Icons.arrow_forward_ios }),
      onTap: () => console.log('개인정보')
    }),
    Divider(),
    ListTile({
      leading: Icon({ icon: Icons.help }),
      title: Text('도움말'),
      trailing: Icon({ icon: Icons.arrow_forward_ios }),
      onTap: () => console.log('도움말')
    })
  ]
});

주의사항

  • Column 내부의 자식 위젯이 세로 공간을 초과하면 오버플로우 오류가 발생합니다. 이런 경우 SingleChildScrollView로 감싸거나 Flexible/Expanded를 사용하세요.
  • crossAxisAlignment.stretch를 사용할 때, 자식 위젯의 너비가 제한되지 않으면 무한히 늘어날 수 있습니다.
  • Column은 스크롤을 지원하지 않습니다. 스크롤이 필요한 경우 SingleChildScrollView 또는 ListView를 사용하세요.
  • 가로 방향 레이아웃이 필요한 경우 Row 위젯을 사용하세요.

관련 위젯

  • Row: 자식 위젯을 가로로 배치
  • Flex: Column과 Row의 기반이 되는 더 유연한 레이아웃 위젯
  • ListView: 스크롤 가능한 세로 리스트
  • Stack: 자식 위젯을 겹쳐서 배치
  • Expanded: Column 내에서 남은 공간을 차지
  • Flexible: Column 내에서 유연한 크기 조정