레이아웃 기초 — Row, Column, Stack, Expanded
레이아웃 기초 — Row, Column, Stack, Expanded
Flutter에서 레이아웃을 잡는 핵심 위젯은 Row, Column, Stack입니다. CSS의 Flexbox와 비슷한 개념이라 웹 개발 경험이 있다면 금방 익숙해질 겁니다.
Column — 세로 배치
자식 위젯들을 위에서 아래로 배치합니다.
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과 축만 반대입니다.
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 안에서 남은 공간을 어떻게 분배할지 결정합니다.
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 차이
| 구분 | Expanded | Flexible |
|---|---|---|
| 빈 공간 | 모두 차지 | 필요한 만큼만 차지 |
| 기본 fit | FlexFit.tight | FlexFit.loose |
| 사용 시점 | 공간을 꽉 채울 때 | 최소 공간만 차지할 때 |
Row(
children: [
// 필요한 만큼만 차지 (남는 공간은 비움)
Flexible(
child: Text('짧은 텍스트'),
),
// 남은 공간 전부 차지
Expanded(
child: Text('이 텍스트는 남은 공간을 전부 사용합니다'),
),
],
)
Stack — 겹쳐서 배치
자식 위젯들을 ** 위로 쌓아 올리듯** 겹쳐서 배치합니다.
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로 정확한 위치 지정
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을 사용하세요.
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')),
],
)
실전 예제: 카드 레이아웃
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 = 가로 배치
MainAxisAlignment와CrossAxisAlignment로 정렬을 조절합니다- Expanded 는 남은 공간을 꽉 채우고, Flexible 은 필요한 만큼만 차지합니다
- Stack 은 위젯을 겹쳐 배치할 때, Positioned 로 정확한 위치를 지정합니다
- 줄바꿈이 필요하면 Row 대신 Wrap 을 사용하세요
- Row, Column, Stack을 중첩하면 대부분의 레이아웃을 구현할 수 있습니다
댓글 로딩 중...