본문 바로가기
Flutter 독학으로 기본기 익히기

04. Flutter 에서 자주 사용하는 레이아웃 위젯

by daco2020 2025. 5. 3.

 

오늘 배울 것

Flutter로 UI 화면을 구현할 때 자주 사용하는 레이아웃 위젯들을 하나하나 소개하고 직접 실습할 수 있는 코드를 제공해드리겠습니다.

 

오늘 배우는 레이아웃 위젯은 다음과 같습니다.

 

1. Column

2. Row

3. Container
4. Stack
5. Expanded
6. Padding
7. SizedBox

 

 

 

 

1. Column (세로 정렬)

Column은 자식 위젯들을 세로로 나열할 때 사용합니다.

Column(
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Text('첫 번째'),
    Text('두 번째'),
    Text('세 번째'),
  ],
)

 

자주 사용하는 속성으로는 다음 두 가지가 있습니다. 

 

- mainAxisAlignment: 주축 정렬 (세로)
- crossAxisAlignment: 교차축 정렬 (가로)

 

이 속성들은 children 배열에 담긴 요소들을 정렬하는 역할을 합니다. 참고로 children은 복수의 요소를 허용하는 배열입니다. 이와 달리 child는 하나의 위젯만을 허용합니다.

 

Column은 기본적으로 요소의 가로 넓이를 따릅니다. 즉, crossAxisAlignment(Column에서는 가로)를 적용하려면 Column의 가로 넓이가 정렬을 수행할 만큼 충분한 공간이 있어야 합니다. (위 이미지는 Column의 가로 넓이를 화면의 넓이만큼 설정한 상태입니다.)

 

 

 

 

2. Row (가로 정렬)

Row는 자식 위젯들을 가로로 나열할 때 사용합니다. 

Row(
  mainAxisAlignment: MainAxisAlignment.spaceAround,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Icon(Icons.star),
    Icon(Icons.favorite),
    Icon(Icons.access_alarm),
  ],
)

 

자주 사용하는 속성은 Column 과 동일합니다. 

 

- mainAxisAlignment: 주축 정렬 (가로)
- crossAxisAlignment: 교차축 정렬 (세로)

 

속성의 값을 하나씩 변경하면서 어떻게 정렬이 되는지 확인해보세요. 예를 들어, mainAxisAlignment.center 는 가운데 정렬이지만 mainAxisAlignment.spaceAround 는 요소간에 간격을 두어 떨어뜨립니다.

 

Row는 요소의 세로 넓이를 따릅니다. 즉, crossAxisAlignment(Row에서는 세로)를 적용하려면 Row의 가로 넓이가 정렬을 수행할 만큼 충분한 공간이 있어야 합니다. (위 이미지는 Row의 세로 넓이를 화면의 넓이만큼 설정한 상태입니다.)

 

 

 

 

3. Container (박스 위젯)

Container은 크기, 패딩, 마진, 배경색 등을 설정할 수 있는 기본 박스 위젯입니다.

Container(
  color: Colors.amber,
  width: 200,
  height: 100,
  padding: const EdgeInsets.all(16),
  margin: const EdgeInsets.all(8),
  child: const Text('Container 안에 있는 텍스트'),
)

 

color는 Container의 배경색을 지정할 수 있습니다.

 

width는 넓이, height는 높이를 지정할 수 있습니다.

 

padding은 내부 여백, margin은 외부 여백을 지정할 수 있습니다.

 

 

 

 

4. Stack (위젯 겹치기)

Stack은 위젯을 서로 겹쳐서 배치하고 싶을 때 사용합니다.

Stack(
  alignment: Alignment.center,
  children: [
    Container(width: 200, height: 200, color: Colors.blue),
    Container(width: 150, height: 150, color: Colors.red),
    Positioned(
      top: 100,
      left: 100,
      child: Container(width: 100, height: 100, color: Colors.green),
    ),
  ],
);

 

alignment 는 요소들 간의 정렬을 지정합니다.

 

children 안에 있는 요소 중에 Positioned 를 이용하면 특정 위치로 요소를 고정시킬 수 있습니다. 아래 이미지에서 green 속성의 Container가 정렬을 무시하고 고정되어 있는 것을 볼 수 있습니다.

 

 

 

 

5. Expanded (남은 공간 채우기)

Expanded은 Row나 Column 안에서 내부 요소들이 남은 공간을 적절히 나누고 싶을 때 사용합니다. (반드시 부모 위젯이 Row 또는 Column이어야 합니다)

Row(
  children: [
    Expanded(child: Container(color: Colors.blue, height: 100)),
    Expanded(child: Container(color: Colors.red, height: 100)),
    Expanded(child: Container(color: Colors.green, height: 100)),
  ],
);

 

위 코드는 가로로 정렬된 자식 요소 blue, red, green 이 화면의 남은 공간을 채우는 코드입니다.

 

 

 

 

6. Padding (여백 주기)

Padding은 감싸고자하는 위젯의 바깥쪽 여백을 줄 때 사용합니다.

Padding(
  padding: EdgeInsets.all(64.0),
  child: Container(
    padding: EdgeInsets.all(32.0),
    color: Colors.amber,
    child: Text('패딩으로 둘러싸인 텍스트'),
  ),
);

 

위 코드는 Padding 을 통해 Container 위젯을 감싸 바깥쪽에 여백을 주는 코드입니다. 그리고 Container 내부에서 padding 속성을 추가해 내부 여백을 주었습니다. (아래 이미지 참고)

 

이렇듯 외부 여백은 Padding 위젯을, 내부 여백은 Container 속성의 padding을 사용하여 표현할 수 있습니다.

 

 

 

 

7. SizedBox (고정 크기/간격 주기)

SizedBoxs는 고정된 크기의 빈 공간이나 위젯에 사이즈를 지정해 줄 때 사용합니다.

Column(
  children: [
    Text('위 텍스트'),
    SizedBox(height: 30),
    Text('아래 텍스트'),
    SizedBox(height: 60),
    SizedBox(
      width: 150,
      height: 50,
      child: ElevatedButton(onPressed: () {}, child: Text('고정 크기')),
    ),
  ],
);

 

위 코드는 height 30 과 height 60 처럼 요소 간에 높이 간격을 주고, 마지막에는 크기를 width 150, height 50 으로 지정한 버튼 요소를 표현합니다.

 

 

 

전체 실습 코드

아래 코드를 main.dart 에 붙여넣기 하고 앱을 실행하면 각 위젯들을 직접 실습해볼 수 있습니다.

import 'package:flutter/material.dart';

void main() {
  runApp(const LayoutDemoApp());
}

class LayoutDemoApp extends StatelessWidget {
  const LayoutDemoApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '레이아웃 위젯 실습',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: const LayoutListPage(),
    );
  }
}

class LayoutListPage extends StatelessWidget {
  const LayoutListPage({super.key});

  @override
  Widget build(BuildContext context) {
    final items = [
      {'title': 'Column', 'widget': const ColumnExample()},
      {'title': 'Row', 'widget': const RowExample()},
      {'title': 'Container', 'widget': const ContainerExample()},
      {'title': 'Stack', 'widget': const StackExample()},
      {'title': 'Expanded', 'widget': const ExpandedExample()},
      {'title': 'Padding', 'widget': const PaddingExample()},
      {'title': 'SizedBox', 'widget': const SizedBoxExample()},
    ];

    return Scaffold(
      appBar: AppBar(title: const Text('레이아웃 위젯 실습')),
      body: ListView(
        children:
            items.map((item) {
              return Card(
                child: ListTile(
                  title: Text(item['title'] as String),
                  trailing: const Icon(Icons.arrow_forward_ios),
                  onTap: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                        builder:
                            (_) => Scaffold(
                              appBar: AppBar(
                                title: Text(item['title'] as String),
                              ),
                              body: item['widget'] as Widget,
                            ),
                      ),
                    );
                  },
                ),
              );
            }).toList(),
      ),
    );
  }
}

// 1. Column
class ColumnExample extends StatelessWidget {
  const ColumnExample({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: double.infinity, // 화면 전체 너비 사용
      height: double.infinity, // 화면 전체 높이 사용
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: const [Text('첫 번째'), Text('두 번째'), Text('세 번째')],
      ),
    );
  }
}

// 2. Row
class RowExample extends StatelessWidget {
  const RowExample({super.key});

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      width: double.infinity, // 화면 전체 너비 사용
      height: double.infinity, // 화면 전체 높이 사용
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceAround,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: const [
          Icon(Icons.star),
          Icon(Icons.favorite),
          Icon(Icons.access_alarm),
        ],
      ),
    );
  }
}

// 3. Container
class ContainerExample extends StatelessWidget {
  const ContainerExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.amber,
      width: 200, // 화면 전체 너비 사용
      height: 100, // 화면 전체 높이 사용
      padding: const EdgeInsets.all(16),
      margin: const EdgeInsets.all(8),
      child: const Text('Container 안에 있는 텍스트'),
    );
  }
}

// 4. Stack
class StackExample extends StatelessWidget {
  const StackExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: [
        Container(width: 200, height: 200, color: Colors.blue),
        Container(width: 150, height: 150, color: Colors.red),
        Positioned(
          top: 100,
          left: 100,
          child: Container(width: 100, height: 100, color: Colors.green),
        ),
      ],
    );
  }
}

// 5. Expanded
class ExpandedExample extends StatelessWidget {
  const ExpandedExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Expanded(child: Container(color: Colors.blue, height: 100)),
        Expanded(child: Container(color: Colors.red, height: 100)),
        Expanded(child: Container(color: Colors.green, height: 100)),
      ],
    );
  }
}

// 6. Padding
class PaddingExample extends StatelessWidget {
  const PaddingExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(64.0),
      child: Container(
        padding: EdgeInsets.all(32.0),
        color: Colors.amber,
        child: Text('패딩으로 둘러싸인 텍스트'),
      ),
    );
  }
}

// 7. SizedBox
class SizedBoxExample extends StatelessWidget {
  const SizedBoxExample({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('위 텍스트'),
        SizedBox(height: 30),
        Text('아래 텍스트'),
        SizedBox(height: 60),
        SizedBox(
          width: 150,
          height: 50,
          child: ElevatedButton(onPressed: () {}, child: Text('고정 크기')),
        ),
      ],
    );
  }
}

 

 

 

Recap

- Column은 세로, Row는 가로로 요소들을 정렬할 때 사용합니다.
- Container는 박스에 스타일을 적용할 때 사용합니다.
- Stack은 위젯들을 겹칠 때 사용합니다.
- Expanded는 남은 공간을 채울 때 사용합니다.
- Padding은 여백을 지정할 때 사용합니다.
- SizedBox 요소의 크기를 지정하거나 요소간의 간격 조절에 사용합니다.