12 KiB
위젯 개념과 주요 위젯
Flutter의 핵심은 위젯(Widget)입니다. Flutter 애플리케이션은 여러 위젯들로 구성되어 있으며, 위젯은 UI의 구성 요소를 나타냅니다. 이 장에서는 Flutter 위젯의 기본 개념과 위젯 시스템의 작동 방식을 알아보겠습니다.
위젯이란?
위젯(Widget)은 Flutter에서 UI를 구성하는 기본 단위입니다. 버튼, 텍스트, 이미지, 레이아웃, 스크롤 등 화면에 보이는 모든 요소는 위젯입니다. Flutter의 철학은 "모든 것이 위젯"이라는 개념에 기반하고 있습니다.
위젯은 다음과 같은 특징을 가지고 있습니다:
- 불변성(Immutable): 위젯은 생성된 후 변경할 수 없습니다. UI를 변경하려면 새로운 위젯을 생성해야 합니다.
- 계층 구조: 위젯은 트리 구조로 조직되며, 부모 위젯은 자식 위젯을 포함할 수 있습니다.
- 선언적 UI: Flutter는 현재 애플리케이션 상태에 따라 UI가 어떻게 보여야 하는지 선언적으로 정의합니다.
- 합성(Composition): 작은 위젯들을 조합하여 복잡한 UI를 구성합니다.
Flutter 위젯의 주기
Flutter 위젯은 생성, 구성, 렌더링의 주기를 거칩니다:
graph TD
A[위젯 생성] --> B[빌드 메서드 호출]
B --> C[요소 트리 생성]
C --> D[RenderObject 생성]
D --> E[화면에 렌더링]
E --> F[위젯 상태 변경]
F --> B
- 위젯 생성: 위젯 클래스의 인스턴스가 생성됩니다.
- 빌드 메서드 호출: 위젯의
build()
메서드가 호출되어 위젯 트리를 구성합니다. - 요소 트리 생성: 위젯 트리를 기반으로 Element 트리가 생성되거나 업데이트됩니다.
- RenderObject 생성: Element 트리에 따라 RenderObject 트리가 생성되거나 업데이트됩니다.
- 화면에 렌더링: RenderObject 트리를 기반으로 UI가 화면에 렌더링됩니다.
- 위젯 상태 변경: 상태 변경 시 위젯이 다시 빌드됩니다.
위젯 유형
Flutter 위젯은 크게 두 가지 유형으로 나눌 수 있습니다:
1. Stateless 위젯
Stateless 위젯은 내부 상태를 가지지 않는 정적인 위젯입니다. 생성 시 전달받은 속성(properties)만 사용하며, 한 번 빌드되면 변경되지 않습니다. 간단한 UI 요소나 변경이 필요 없는 화면에 적합합니다.
class GreetingWidget extends StatelessWidget {
final String name;
const GreetingWidget({Key? key, required this.name}) : super(key: key);
@override
Widget build(BuildContext context) {
return Text('안녕하세요, $name님!');
}
}
2. Stateful 위젯
Stateful 위젯은 내부 상태를 가지고 있으며, 상태가 변경되면 UI가 다시 빌드됩니다. 사용자 입력, 네트워크 응답, 시간 경과 등에 따라 변경되는 UI 요소에 적합합니다.
class CounterWidget extends StatefulWidget {
const CounterWidget({Key? key}) : super(key: key);
@override
_CounterWidgetState createState() => _CounterWidgetState();
}
class _CounterWidgetState extends State<CounterWidget> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('카운터: $_counter'),
ElevatedButton(
onPressed: _incrementCounter,
child: Text('증가'),
),
],
);
}
}
위젯 트리
Flutter 애플리케이션의 UI는 위젯 트리로 표현됩니다. 모든 Flutter 앱은 루트 위젯에서 시작하여 자식 위젯들로 구성된 트리 구조를 가집니다.
graph TD
A[MaterialApp] --> B[Scaffold]
B --> C[AppBar]
B --> D[Body: Column]
D --> E[Text]
D --> F[Button]
D --> G[Image]
위젯 트리의 특징:
- 부모-자식 관계: 위젯은 하나의 부모와 여러 자식을 가질 수 있습니다.
- 단방향 데이터 흐름: 데이터는 부모에서 자식으로 흐릅니다.
- 렌더링 최적화: Flutter는 변경된 위젯만 다시 빌드하여 성능을 최적화합니다.
위젯의 세 가지 트리
Flutter는 UI 렌더링을 위해 세 가지 트리를 관리합니다:
graph TD
W[Widget 트리] --> E[Element 트리]
E --> R[RenderObject 트리]
R --> S[화면 렌더링]
- Widget 트리: UI의 설계도로, 사용자가 작성한 위젯 클래스의 인스턴스들로 구성됩니다.
- Element 트리: Widget과 RenderObject를 연결하는 중간 계층으로, 위젯의 수명 주기를 관리합니다.
- RenderObject 트리: 실제 화면에 그려지는 객체들을 표현하며, 레이아웃 계산과 페인팅을 담당합니다.
Flutter 위젯의 구성 방식
Flutter 위젯은 합성(Composition)을 통해 구성됩니다. 작은 위젯들을 조합하여 복잡한 UI를 만들 수 있습니다.
Scaffold(
appBar: AppBar(
title: Text('Flutter 앱'),
actions: [
IconButton(
icon: Icon(Icons.settings),
onPressed: () {},
),
],
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Hello, Flutter!'),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {},
child: Text('버튼'),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
)
이 예제에서 Scaffold
, AppBar
, Text
, Center
, Column
등 여러 위젯들이 중첩되어 하나의 화면을 구성합니다.
Flutter 기본 위젯의 분류
Flutter 위젯은 기능에 따라 여러 카테고리로 분류할 수 있습니다:
graph TD
A[Flutter 위젯] --> B[구조적 위젯]
A --> C[시각적 위젯]
A --> D[레이아웃 위젯]
A --> E[입력 위젯]
A --> F[상호작용 위젯]
A --> G[애니메이션 위젯]
B --> B1[Scaffold]
B --> B2[AppBar]
C --> C1[Text]
C --> C2[Image]
C --> C3[Icon]
D --> D1[Container]
D --> D2[Row/Column]
D --> D3[Stack]
E --> E1[TextField]
E --> E2[Checkbox]
E --> E3[Radio]
F --> F1[GestureDetector]
F --> F2[InkWell]
G --> G1[AnimatedContainer]
G --> G2[Hero]
구조적 위젯
애플리케이션의 구조를 정의하는 위젯입니다:
- MaterialApp: Material Design 앱의 진입점
- CupertinoApp: iOS 스타일 앱의 진입점
- Scaffold: 기본 앱 구조 제공 (앱바, 드로워, 바텀 시트 등)
- AppBar: 앱 상단의 앱 바
시각적 위젯
화면에 콘텐츠를 표시하는 위젯입니다:
- Text: 텍스트 표시
- Image: 이미지 표시
- Icon: 아이콘 표시
- Card: 둥근 모서리와 그림자가 있는 카드
레이아웃 위젯
위젯들을 배치하고 정렬하는 위젯입니다:
- Container: 패딩, 마진, 배경색, 크기 등을 설정할 수 있는 범용 컨테이너
- Row/Column: 위젯을 가로/세로로 배열
- Stack: 위젯들을 겹쳐서 배치
- ListView: 스크롤 가능한 목록
입력 위젯
사용자 입력을 받는 위젯입니다:
- TextField: 텍스트 입력
- Checkbox: 체크박스
- Radio: 라디오 버튼
- Slider: 슬라이더
상호작용 위젯
사용자 상호작용을 처리하는 위젯입니다:
- GestureDetector: 다양한 제스처 인식
- InkWell: 터치 효과가 있는 영역
- Draggable: 드래그 가능한 위젯
애니메이션 위젯
애니메이션 효과를 제공하는 위젯입니다:
- AnimatedContainer: 속성 변경 시 애니메이션 효과
- Hero: 화면 전환 시 애니메이션 효과
- FadeTransition: 페이드 인/아웃 효과
위젯 속성 전달
Flutter 위젯은 생성자를 통해 속성을 전달받아 UI를 구성합니다:
Container(
width: 200,
height: 100,
margin: EdgeInsets.all(10),
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black26,
offset: Offset(0, 2),
blurRadius: 6,
),
],
),
child: Center(
child: Text(
'Flutter',
style: TextStyle(
color: Colors.white,
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
),
)
이 예제에서 Container
, Center
, Text
위젯은 각각 다양한 속성(width, height, decoration, style 등)을 전달받아 특정한 모양과 스타일을 가진 UI를 생성합니다.
위젯 키(Key)
위젯 키는 Flutter가 위젯 트리에서 위젯을 식별하는 데 사용됩니다. 특히 동적으로 변경되는 위젯 목록에서 중요한 역할을 합니다.
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(
key: ValueKey(items[index].id), // 고유 ID를 키로 사용
title: Text(items[index].title),
);
},
)
위젯 키의 주요 종류:
- ValueKey: 단일 값을 기반으로 한 키
- ObjectKey: 객체 식별자를 기반으로 한 키
- UniqueKey: 매번 고유한 키를 생성
- GlobalKey: 전역적으로 접근 가능한 키, 위젯의 상태에 접근하거나 위젯의 크기/위치를 파악하는 데 사용
위젯 제약 조건(Constraints)
Flutter의 레이아웃 시스템은 부모 위젯이 자식 위젯에게 제약 조건(constraints)을 전달하고, 자식 위젯은 이 제약 조건 내에서 자신의 크기를 결정하는 방식으로 작동합니다.
graph TD
A[부모 위젯] -->|제약 조건 전달| B[자식 위젯]
B -->|크기 결정| B
B -->|위치 보고| A
제약 조건은 최소/최대 너비와 높이로 구성되며, 자식 위젯은 이 범위 내에서 크기를 결정합니다:
ConstrainedBox(
constraints: BoxConstraints(
minWidth: 100,
maxWidth: 200,
minHeight: 50,
maxHeight: 100,
),
child: Container(
color: Colors.blue,
width: 150, // minWidth와 maxWidth 사이의 값
height: 75, // minHeight와 maxHeight 사이의 값
),
)
위젯 렌더링 과정
Flutter의 위젯 렌더링 과정은 다음과 같습니다:
- 레이아웃 단계: 부모 위젯이 자식 위젯에게 제약 조건을 전달하고, 자식 위젯은 자신의 크기를 결정합니다.
- 페인팅 단계: 위젯의 외관이 렌더링됩니다.
- 합성 단계: 렌더링된 레이어들이 화면에 합성됩니다.
sequenceDiagram
참가자 Widget
참가자 Element
참가자 RenderObject
참가자 Screen
Widget->>Element: 생성/업데이트
Element->>RenderObject: 생성/업데이트
RenderObject->>RenderObject: 레이아웃 계산
RenderObject->>RenderObject: 페인팅
RenderObject->>Screen: 합성 및 표시
결론
Flutter의 위젯 시스템은 UI 구성을 위한 강력하고 유연한 방법을 제공합니다. "모든 것이 위젯"이라는 철학을 통해 일관된 방식으로 복잡한 UI를 구축할 수 있습니다. 위젯의 불변성, 선언적 특성, 합성 패턴은 Flutter가 효율적이고 예측 가능한 UI 프레임워크가 되는 데 중요한 역할을 합니다.
다음 장에서는 Stateless 위젯과 Stateful 위젯에 대해 더 자세히 알아보겠습니다.