레이아웃 기초 — Row, Column, Stack, Expanded

Flutter에서 레이아웃을 잡는 핵심 위젯은 Row, Column, Stack입니다. CSS의 Flexbox와 비슷한 개념이라 웹 개발 경험이 있다면 금방 익숙해질 겁니다.


Column — 세로 배치

자식 위젯들을 위에서 아래로 배치합니다.

DART
Column(
  // 세로축(main axis) 정렬
  mainAxisAlignment: MainAxisAlignment.center,
  // 가로축(cross axis) 정렬
  crossAxisAlignment: CrossAxisAlignment.start,
  // 세로 크기를 자식 크기에 맞출지, 최대로 할지
  mainAxisSize: MainAxisSize.min,
  children: const [
    Text('첫 번째'),
    Text('두 번째'),
    Text('세 번째'),
  ],
)

MainAxisAlignment 옵션

설명
start시작점에 모음 (기본)
center중앙 정렬
end끝점에 모음
spaceBetween양 끝 붙이고 균등 분배
spaceAround양 끝 절반 간격 + 균등 분배
spaceEvenly모든 간격 동일

Row — 가로 배치

자식 위젯들을 ** 왼쪽에서 오른쪽으로** 배치합니다. Column과 축만 반대입니다.

DART
Row(
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    _buildIcon(Icons.home, '홈'),
    _buildIcon(Icons.search, '검색'),
    _buildIcon(Icons.person, '프로필'),
  ],
)

Widget _buildIcon(IconData icon, String label) {
  return Column(
    children: [
      Icon(icon, size: 32),
      const SizedBox(height: 4),
      Text(label),
    ],
  );
}

Row와 Column은 중첩해서 사용하는 것이 일반적입니다.


Expanded와 Flexible

Row나 Column 안에서 남은 공간을 어떻게 분배할지 결정합니다.

DART
Row(
  children: [
    // 남은 공간의 2/3 차지
    Expanded(
      flex: 2,
      child: Container(
        color: Colors.blue,
        height: 100,
        child: const Center(child: Text('2')),
      ),
    ),
    // 남은 공간의 1/3 차지
    Expanded(
      flex: 1,
      child: Container(
        color: Colors.red,
        height: 100,
        child: const Center(child: Text('1')),
      ),
    ),
  ],
)

Expanded vs Flexible 차이

구분ExpandedFlexible
빈 공간모두 차지필요한 만큼만 차지
기본 fitFlexFit.tightFlexFit.loose
사용 시점공간을 꽉 채울 때최소 공간만 차지할 때
DART
Row(
  children: [
    // 필요한 만큼만 차지 (남는 공간은 비움)
    Flexible(
      child: Text('짧은 텍스트'),
    ),
    // 남은 공간 전부 차지
    Expanded(
      child: Text('이 텍스트는 남은 공간을 전부 사용합니다'),
    ),
  ],
)

Stack — 겹쳐서 배치

자식 위젯들을 ** 위로 쌓아 올리듯** 겹쳐서 배치합니다.

DART
Stack(
  alignment: Alignment.center,
  children: [
    // 맨 아래 레이어
    Container(
      width: 200,
      height: 200,
      color: Colors.blue,
    ),
    // 중간 레이어
    Container(
      width: 150,
      height: 150,
      color: Colors.red,
    ),
    // 맨 위 레이어
    const Text(
      'Stack!',
      style: TextStyle(
        color: Colors.white,
        fontSize: 24,
      ),
    ),
  ],
)

Positioned로 정확한 위치 지정

DART
Stack(
  children: [
    // 배경 이미지
    Image.network('https://example.com/bg.jpg'),

    // 좌상단 태그
    Positioned(
      top: 8,
      left: 8,
      child: Container(
        padding: const EdgeInsets.symmetric(
          horizontal: 8,
          vertical: 4,
        ),
        decoration: BoxDecoration(
          color: Colors.black54,
          borderRadius: BorderRadius.circular(4),
        ),
        child: const Text(
          'NEW',
          style: TextStyle(color: Colors.white),
        ),
      ),
    ),

    // 우하단 좋아요 버튼
    Positioned(
      bottom: 8,
      right: 8,
      child: IconButton(
        icon: const Icon(Icons.favorite, color: Colors.red),
        onPressed: () {},
      ),
    ),
  ],
)

Stack은 프로필 카드, 배지, 오버레이 UI 등에서 자주 사용합니다.


Wrap — 자동 줄바꿈

Row는 공간이 부족하면 overflow 에러가 발생합니다. 자동으로 줄바꿈이 필요하면 Wrap을 사용하세요.

DART
Wrap(
  spacing: 8,         // 가로 간격
  runSpacing: 8,      // 줄 간격
  children: const [
    Chip(label: Text('Flutter')),
    Chip(label: Text('Dart')),
    Chip(label: Text('Mobile')),
    Chip(label: Text('Cross-Platform')),
    Chip(label: Text('UI')),
  ],
)

실전 예제: 카드 레이아웃

DART
Card(
  child: Padding(
    padding: const EdgeInsets.all(16),
    child: Row(
      children: [
        // 왼쪽: 프로필 이미지
        const CircleAvatar(
          radius: 30,
          child: Icon(Icons.person),
        ),
        const SizedBox(width: 16),
        // 중앙: 이름과 설명
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: const [
              Text(
                '심정훈',
                style: TextStyle(
                  fontSize: 18,
                  fontWeight: FontWeight.bold,
                ),
              ),
              SizedBox(height: 4),
              Text(
                'Flutter 개발자',
                style: TextStyle(color: Colors.grey),
              ),
            ],
          ),
        ),
        // 오른쪽: 액션 버튼
        IconButton(
          icon: const Icon(Icons.message),
          onPressed: () {},
        ),
      ],
    ),
  ),
)

정리

  • Column = 세로 배치, Row = 가로 배치
  • MainAxisAlignmentCrossAxisAlignment로 정렬을 조절합니다
  • Expanded 는 남은 공간을 꽉 채우고, Flexible 은 필요한 만큼만 차지합니다
  • Stack 은 위젯을 겹쳐 배치할 때, Positioned 로 정확한 위치를 지정합니다
  • 줄바꿈이 필요하면 Row 대신 Wrap 을 사용하세요
  • Row, Column, Stack을 중첩하면 대부분의 레이아웃을 구현할 수 있습니다
댓글 로딩 중...