출시·고도화 중
Flutter 안내서 · 3/6
Flutter를 이해하는 출발점은 "모든 것이 위젯"이라는 생각입니다. 글자, 버튼, 여백, 정렬, 화면 전체, 앱 자체까지 모두 위젯이며, 작은 위젯을 조합해 큰 위젯을 만드는 방식으로 화면을 구성합니다. 이 장에서는 위젯의 두 종류, build 메서드, 레이아웃, 디자인 위젯 모음, 화면 이동을 차례로 살펴봅니다.
위젯은 화면의 한 부분이 어떻게 보여야 하는지를 적은 가벼운 설정 객체입니다. 위젯은 불변(immutable)이며, 상태가 바뀌면 Flutter는 새 위젯 트리를 만들고 이전 트리와 비교해 실제로 바뀐 부분만 다시 그립니다. 그래서 위젯을 자주 새로 만드는 것은 문제가 되지 않습니다.
Flutter는 운영체제의 기본 UI 부품을 쓰지 않고 자체 렌더링 엔진으로 모든 픽셀을 직접 그립니다. 덕분에 플랫폼마다 화면이 똑같이 보이고, 애니메이션과 사용자 정의 디자인을 자유롭게 만들 수 있습니다.
StatelessWidget은 생성할 때 받은 값만으로 화면을 그리는 위젯입니다. 스스로 바뀌는 데이터가 없습니다.
class Greeting extends StatelessWidget {
const Greeting({super.key, required this.name});
final String name;
@override
Widget build(BuildContext context) {
return Text('안녕하세요, $name님', style: Theme.of(context).textTheme.headlineSmall);
}
}StatefulWidget은 시간이 지나며 바뀌는 상태를 가집니다. 위젯 클래스와 상태를 담는 State 클래스 두 개로 나뉘며, 상태를 바꿀 때는 반드시 setState 안에서 바꿔야 Flutter가 다시 그립니다.
class Counter extends StatefulWidget {
const Counter({super.key});
@override
State<Counter> createState() => _CounterState();
}
class _CounterState extends State<Counter> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text('눌린 횟수: $_count'),
FilledButton(
onPressed: () => setState(() => _count++),
child: const Text('더하기'),
),
],
);
}
}State 객체는 위젯이 다시 만들어져도 유지됩니다. 처음 한 번 준비할 일은 initState에서, 컨트롤러나 구독을 정리하는 일은 dispose에서 합니다.
build는 위젯이 화면에 어떻게 보일지 반환하는 메서드로, 상태가 바뀔 때마다 여러 번 호출될 수 있습니다. 그래서 build 안에서는 네트워크 요청이나 무거운 계산을 하지 않고, 이미 준비된 데이터를 위젯으로 바꾸는 일만 합니다.
BuildContext는 위젯 트리에서 현재 위젯의 위치입니다. Theme.of(context), MediaQuery.sizeOf(context), Navigator.of(context)처럼 위쪽 트리에 있는 정보를 찾을 때 씁니다. 바뀌지 않는 위젯 앞에 const를 붙이면 다시 만들지 않아도 되므로 성능에 도움이 됩니다.
Flutter의 레이아웃도 위젯입니다. 자주 쓰는 위젯은 다음과 같습니다.
| 위젯 | 역할 |
|---|---|
Row, Column | 자식을 가로·세로로 나열 |
Expanded, Flexible | Row·Column 안에서 남은 공간 나누기 |
Stack, Positioned | 위젯을 겹쳐 배치 |
Padding, SizedBox, Center, Align | 여백, 크기, 정렬 |
Container | 여백, 배경, 테두리, 크기를 한 번에 |
ListView, GridView | 스크롤되는 목록과 격자 |
레이아웃의 기본 규칙은 "제약은 아래로, 크기는 위로, 위치는 부모가 정한다"입니다. 부모가 자식에게 최소·최대 크기를 알려 주면 자식이 그 안에서 크기를 정하고, 부모가 위치를 정합니다.
Widget buildProfile() {
return Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
const CircleAvatar(radius: 24, child: Icon(Icons.person)),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
Text('홍길동', style: TextStyle(fontWeight: FontWeight.bold)),
Text('Flutter 개발자'),
],
),
),
IconButton(onPressed: () {}, icon: const Icon(Icons.more_vert)),
],
),
);
}항목이 많은 목록은 ListView.builder를 써서 화면에 보이는 항목만 만들도록 합니다.
Flutter는 두 가지 디자인 위젯 모음을 제공합니다. Material은 Google의 Material Design(기본은 Material 3)을 따르며 MaterialApp, Scaffold, AppBar, FilledButton 등이 있습니다. Cupertino는 iOS 스타일로 CupertinoApp, CupertinoButton, CupertinoNavigationBar 등이 있습니다.
MaterialApp(
title: '할 일',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
),
darkTheme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo, brightness: Brightness.dark),
),
home: Scaffold(
appBar: AppBar(title: const Text('할 일')),
body: const Center(child: Text('목록이 비어 있습니다')),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: const Icon(Icons.add),
),
),
);한 앱 안에서 두 모음을 섞어 쓸 수도 있습니다. Switch.adaptive처럼 플랫폼에 맞춰 모양을 바꾸는 위젯도 있습니다.
기본 화면 이동은 Navigator가 맡습니다. 새 화면을 스택에 넣으면(push) 위에 쌓이고, 빼면(pop) 이전 화면으로 돌아갑니다.
// 상세 화면으로 이동
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const DetailScreen()),
);
// 이전 화면으로 돌아가기
Navigator.of(context).pop();주소 기반 라우팅, 딥 링크, 웹 주소 표시줄 연동이 필요하면 Flutter 팀이 관리하는 go_router 패키지를 많이 씁니다.
setState를 씁니다.build는 자주 호출되므로 가볍게 유지하고, 바뀌지 않는 위젯에는 const를 붙입니다.
댓글 0개
로그인 · 로그인하면 댓글을 남길 수 있습니다.
첫 댓글을 남겨 보세요.