learn-flutter/content/docs/part3/widgets.md
ChangJoo Park(박창주) 900b0ab68b update markdown
2025-05-14 09:34:13 +09:00

12 KiB

위젯 개념과 주요 위젯

Flutter의 핵심은 위젯(Widget)입니다. Flutter 애플리케이션은 여러 위젯들로 구성되어 있으며, 위젯은 UI의 구성 요소를 나타냅니다. 이 장에서는 Flutter 위젯의 기본 개념과 위젯 시스템의 작동 방식을 알아보겠습니다.

위젯이란?

위젯(Widget)은 Flutter에서 UI를 구성하는 기본 단위입니다. 버튼, 텍스트, 이미지, 레이아웃, 스크롤 등 화면에 보이는 모든 요소는 위젯입니다. Flutter의 철학은 "모든 것이 위젯"이라는 개념에 기반하고 있습니다.

위젯은 다음과 같은 특징을 가지고 있습니다:

  1. 불변성(Immutable): 위젯은 생성된 후 변경할 수 없습니다. UI를 변경하려면 새로운 위젯을 생성해야 합니다.
  2. 계층 구조: 위젯은 트리 구조로 조직되며, 부모 위젯은 자식 위젯을 포함할 수 있습니다.
  3. 선언적 UI: Flutter는 현재 애플리케이션 상태에 따라 UI가 어떻게 보여야 하는지 선언적으로 정의합니다.
  4. 합성(Composition): 작은 위젯들을 조합하여 복잡한 UI를 구성합니다.

Flutter 위젯의 주기

Flutter 위젯은 생성, 구성, 렌더링의 주기를 거칩니다:

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 요소나 변경이 필요 없는 화면에 적합합니다.

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]

위젯 트리의 특징:

  1. 부모-자식 관계: 위젯은 하나의 부모와 여러 자식을 가질 수 있습니다.
  2. 단방향 데이터 흐름: 데이터는 부모에서 자식으로 흐릅니다.
  3. 렌더링 최적화: Flutter는 변경된 위젯만 다시 빌드하여 성능을 최적화합니다.

위젯의 세 가지 트리

Flutter는 UI 렌더링을 위해 세 가지 트리를 관리합니다:

graph TD
    W[Widget 트리] --> E[Element 트리]
    E --> R[RenderObject 트리]
    R --> S[화면 렌더링]
  1. Widget 트리: UI의 설계도로, 사용자가 작성한 위젯 클래스의 인스턴스들로 구성됩니다.
  2. Element 트리: Widget과 RenderObject를 연결하는 중간 계층으로, 위젯의 수명 주기를 관리합니다.
  3. 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의 위젯 렌더링 과정은 다음과 같습니다:

  1. 레이아웃 단계: 부모 위젯이 자식 위젯에게 제약 조건을 전달하고, 자식 위젯은 자신의 크기를 결정합니다.
  2. 페인팅 단계: 위젯의 외관이 렌더링됩니다.
  3. 합성 단계: 렌더링된 레이어들이 화면에 합성됩니다.
sequenceDiagram
    참가자 Widget
    참가자 Element
    참가자 RenderObject
    참가자 Screen

    Widget->>Element: 생성/업데이트
    Element->>RenderObject: 생성/업데이트
    RenderObject->>RenderObject: 레이아웃 계산
    RenderObject->>RenderObject: 페인팅
    RenderObject->>Screen: 합성 및 표시

결론

Flutter의 위젯 시스템은 UI 구성을 위한 강력하고 유연한 방법을 제공합니다. "모든 것이 위젯"이라는 철학을 통해 일관된 방식으로 복잡한 UI를 구축할 수 있습니다. 위젯의 불변성, 선언적 특성, 합성 패턴은 Flutter가 효율적이고 예측 가능한 UI 프레임워크가 되는 데 중요한 역할을 합니다.

다음 장에서는 Stateless 위젯과 Stateful 위젯에 대해 더 자세히 알아보겠습니다.