mirror of
https://github.com/ChangJoo-Park/learn-flutter.git
synced 2025-06-10 14:41:37 +00:00
368 lines
12 KiB
Markdown
368 lines
12 KiB
Markdown
# 위젯 개념과 주요 위젯
|
|
|
|
Flutter의 핵심은 위젯(Widget)입니다. Flutter 애플리케이션은 여러 위젯들로 구성되어 있으며, 위젯은 UI의 구성 요소를 나타냅니다. 이 장에서는 Flutter 위젯의 기본 개념과 위젯 시스템의 작동 방식을 알아보겠습니다.
|
|
|
|
## 위젯이란?
|
|
|
|
위젯(Widget)은 Flutter에서 UI를 구성하는 기본 단위입니다. 버튼, 텍스트, 이미지, 레이아웃, 스크롤 등 화면에 보이는 모든 요소는 위젯입니다. Flutter의 철학은 "모든 것이 위젯"이라는 개념에 기반하고 있습니다.
|
|
|
|
위젯은 다음과 같은 특징을 가지고 있습니다:
|
|
|
|
1. **불변성(Immutable)**: 위젯은 생성된 후 변경할 수 없습니다. UI를 변경하려면 새로운 위젯을 생성해야 합니다.
|
|
2. **계층 구조**: 위젯은 트리 구조로 조직되며, 부모 위젯은 자식 위젯을 포함할 수 있습니다.
|
|
3. **선언적 UI**: Flutter는 현재 애플리케이션 상태에 따라 UI가 어떻게 보여야 하는지 선언적으로 정의합니다.
|
|
4. **합성(Composition)**: 작은 위젯들을 조합하여 복잡한 UI를 구성합니다.
|
|
|
|
## Flutter 위젯의 주기
|
|
|
|
Flutter 위젯은 생성, 구성, 렌더링의 주기를 거칩니다:
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[위젯 생성] --> B[빌드 메서드 호출]
|
|
B --> C[요소 트리 생성]
|
|
C --> D[RenderObject 생성]
|
|
D --> E[화면에 렌더링]
|
|
E --> F[위젯 상태 변경]
|
|
F --> B
|
|
```
|
|
|
|
1. **위젯 생성**: 위젯 클래스의 인스턴스가 생성됩니다.
|
|
2. **빌드 메서드 호출**: 위젯의 `build()` 메서드가 호출되어 위젯 트리를 구성합니다.
|
|
3. **요소 트리 생성**: 위젯 트리를 기반으로 Element 트리가 생성되거나 업데이트됩니다.
|
|
4. **RenderObject 생성**: Element 트리에 따라 RenderObject 트리가 생성되거나 업데이트됩니다.
|
|
5. **화면에 렌더링**: RenderObject 트리를 기반으로 UI가 화면에 렌더링됩니다.
|
|
6. **위젯 상태 변경**: 상태 변경 시 위젯이 다시 빌드됩니다.
|
|
|
|
## 위젯 유형
|
|
|
|
Flutter 위젯은 크게 두 가지 유형으로 나눌 수 있습니다:
|
|
|
|
### 1. Stateless 위젯
|
|
|
|
Stateless 위젯은 내부 상태를 가지지 않는 정적인 위젯입니다. 생성 시 전달받은 속성(properties)만 사용하며, 한 번 빌드되면 변경되지 않습니다. 간단한 UI 요소나 변경이 필요 없는 화면에 적합합니다.
|
|
|
|
```dart
|
|
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 요소에 적합합니다.
|
|
|
|
```dart
|
|
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 앱은 루트 위젯에서 시작하여 자식 위젯들로 구성된 트리 구조를 가집니다.
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[MaterialApp] --> B[Scaffold]
|
|
B --> C[AppBar]
|
|
B --> D[Body: Column]
|
|
D --> E[Text]
|
|
D --> F[Button]
|
|
D --> G[Image]
|
|
```
|
|
|
|
위젯 트리의 특징:
|
|
|
|
1. **부모-자식 관계**: 위젯은 하나의 부모와 여러 자식을 가질 수 있습니다.
|
|
2. **단방향 데이터 흐름**: 데이터는 부모에서 자식으로 흐릅니다.
|
|
3. **렌더링 최적화**: Flutter는 변경된 위젯만 다시 빌드하여 성능을 최적화합니다.
|
|
|
|
## 위젯의 세 가지 트리
|
|
|
|
Flutter는 UI 렌더링을 위해 세 가지 트리를 관리합니다:
|
|
|
|
```mermaid
|
|
graph TD
|
|
W[Widget 트리] --> E[Element 트리]
|
|
E --> R[RenderObject 트리]
|
|
R --> S[화면 렌더링]
|
|
```
|
|
|
|
1. **Widget 트리**: UI의 설계도로, 사용자가 작성한 위젯 클래스의 인스턴스들로 구성됩니다.
|
|
2. **Element 트리**: Widget과 RenderObject를 연결하는 중간 계층으로, 위젯의 수명 주기를 관리합니다.
|
|
3. **RenderObject 트리**: 실제 화면에 그려지는 객체들을 표현하며, 레이아웃 계산과 페인팅을 담당합니다.
|
|
|
|
## Flutter 위젯의 구성 방식
|
|
|
|
Flutter 위젯은 합성(Composition)을 통해 구성됩니다. 작은 위젯들을 조합하여 복잡한 UI를 만들 수 있습니다.
|
|
|
|
```dart
|
|
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 위젯은 기능에 따라 여러 카테고리로 분류할 수 있습니다:
|
|
|
|
```mermaid
|
|
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를 구성합니다:
|
|
|
|
```dart
|
|
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가 위젯 트리에서 위젯을 식별하는 데 사용됩니다. 특히 동적으로 변경되는 위젯 목록에서 중요한 역할을 합니다.
|
|
|
|
```dart
|
|
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)을 전달하고, 자식 위젯은 이 제약 조건 내에서 자신의 크기를 결정하는 방식으로 작동합니다.
|
|
|
|
```mermaid
|
|
graph TD
|
|
A[부모 위젯] -->|제약 조건 전달| B[자식 위젯]
|
|
B -->|크기 결정| B
|
|
B -->|위치 보고| A
|
|
```
|
|
|
|
제약 조건은 최소/최대 너비와 높이로 구성되며, 자식 위젯은 이 범위 내에서 크기를 결정합니다:
|
|
|
|
```dart
|
|
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의 위젯 렌더링 과정은 다음과 같습니다:
|
|
|
|
1. **레이아웃 단계**: 부모 위젯이 자식 위젯에게 제약 조건을 전달하고, 자식 위젯은 자신의 크기를 결정합니다.
|
|
2. **페인팅 단계**: 위젯의 외관이 렌더링됩니다.
|
|
3. **합성 단계**: 렌더링된 레이어들이 화면에 합성됩니다.
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
참가자 Widget
|
|
참가자 Element
|
|
참가자 RenderObject
|
|
참가자 Screen
|
|
|
|
Widget->>Element: 생성/업데이트
|
|
Element->>RenderObject: 생성/업데이트
|
|
RenderObject->>RenderObject: 레이아웃 계산
|
|
RenderObject->>RenderObject: 페인팅
|
|
RenderObject->>Screen: 합성 및 표시
|
|
```
|
|
|
|
## 결론
|
|
|
|
Flutter의 위젯 시스템은 UI 구성을 위한 강력하고 유연한 방법을 제공합니다. "모든 것이 위젯"이라는 철학을 통해 일관된 방식으로 복잡한 UI를 구축할 수 있습니다. 위젯의 불변성, 선언적 특성, 합성 패턴은 Flutter가 효율적이고 예측 가능한 UI 프레임워크가 되는 데 중요한 역할을 합니다.
|
|
|
|
다음 장에서는 Stateless 위젯과 Stateful 위젯에 대해 더 자세히 알아보겠습니다.
|