플러터와 Stateful Widget

2023. 10. 31. 16:03Frontend/Dart&Flutter

Stateless vs. Stateful

  • Stateless Widget : build 메서드를 통해 단지 UI를 출력함.
  • Stateful Widget : 상태에 따라 변화하는 데이터를 생각함. 데이터가 변화될때마다 UI가 변경됨. 위젯에 데이터 저장후 실시간으로 데이터 변화를 볼 수 있음.
    • 상태가 없는 위젯 그 자체
    • 위젯의 상태로 state는 위젯에 들어갈 데이터와 UI를 넣는 곳. 데이터가 변경되면 해당 위젯의 UI도 변경됨.

Stateful Widget을 만드는 법

  1. st만 작성시 stateless 또는 stateful 위젯을 선택할 수 있음
  2. stateless widget에서 staeful 위젯으로 변경. 코드 액션 : convert to stateful widget

Stateful Widget의 특성

  • stateful widget의 데이터는 단지 dart 클래스 프로퍼티일 뿐.
class App extends StatefulWidget {
  // extend됨
  const App({super.key});
  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  // State 가 변경시 UI 가 변경됨. 
  @override
  Widget build(BuildContext context) {
  • 예제
class _AppState extends State<App> {
  int counter = 0;

  void onClicked() {
    setState(() {
      counter = counter + 1;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            backgroundColor: const Color(0xFFF4EDDB),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                  const Text('Click Count', style: TextStyle(fontSize: 30)),
                  Text('$counter', style: const TextStyle(fontSize: 30)),
                  IconButton(
                      iconSize: 40,
                      onPressed: onClicked,
                      icon: const Icon(Icons.add_box_rounded))
                ]))));
  }

setState 함수

  • state 클래스에게 데이터가 변경되었다고 알림. setState함수를 통하지 않고는 build 메서드가 수행되지 않음. 새로운 데이터와 함께 build 메서드를 호출함.
// setState 안에서 변화시킴 (추천)
void onClicked() {
    setState(() {
      counter = counter + 1;
    });
}
// 다른 방식의 사용법 : 데이터의 변화를 setState 안에 무조건 넣을 필요는 없음.
void onClicked() {
    counter = counter + 1;
    setState(() {});
}
  • 예제 : 숫자 배열 표시하기
class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  List<int> numbers = [];

  void onClicked() {
    setState(() {
      numbers.add(numbers.length);
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            backgroundColor: const Color(0xFFF4EDDB),
            body: Center(
                child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                  const Text('Click Count', style: TextStyle(fontSize: 30)),
                  for (var n in numbers) Text('$n'),
                  IconButton(
                      iconSize: 40,
                      onPressed: onClicked,
                      icon: const Icon(Icons.add_box_rounded))
                ]))));
  }
}

BuildContext

  • 위젯 트리
    • App 루트 아래로 자식들을 가짐.
    • 자식요소가 부모 요소(MaterialApp)에 접근
    • BuildContext의 context : Text 이전에 있는 모든 상위 요소에 대한 정보. 부모 요소들의 모든 정보를 담고 있음. context를 통해 먼 곳의 요소에 접근할 수 있음.
  • theme : default style. 앱의 스타일을 한 곳에서 다룰 수 있음. 일일이 복붙, 타이핑하지 않아도 됨.

StatefulWidget의 라이프사이클

  • initState : build 이전에 호출. 변수를 초기화하고 API 업데이트를 구독할 수 있도록 함. 부모요소에 의존하는 데이터를 초기화하기 위해 사용. 단 한번만 호출됨. build 보다 먼저 호출되어야함.
@override
  void initState() {
    super.initState();
  }
  • build : 위젯에서 UI를 만듦
  • dispose : 위젯이 제거될 때. 이벤트 리스너 같은 것들을 구독 취소하는 것. 위젯이 스크린에서 제거될 때 호출되는 메서드. api 업데이트나 이벤트 리스터로부터 구독을 취소하거나 form의 리스터로부터 벗어나고 싶을 때. 취소하고 싶을 때. 위젯트리에서 제거되기 전에 무언가를 취소하고 싶을 때
  • 예제
import 'package:flutter/material.dart';

void main() {
  runApp(const App());
}

class App extends StatefulWidget {
  const App({super.key});

  @override
  State<App> createState() => _AppState();
}

class _AppState extends State<App> {
  bool showTitle = true;

  void toggleTitle() {
    setState(() {
      showTitle = !showTitle;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        textTheme: const TextTheme(
          titleLarge: TextStyle(color: Colors.red),
        ),
      ),
      home: Scaffold(
        backgroundColor: const Color(0xFFF4EDDB),
        body: Center(
          child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [
            showTitle ? const MyLargeTitle() : const Text('nothing'),
            IconButton(
                onPressed: toggleTitle, icon: const Icon(Icons.remove_red_eye))
          ]),
        ),
      ),
    );
  }
}

class MyLargeTitle extends StatefulWidget {
  const MyLargeTitle({
    super.key,
  });

  @override
  State<MyLargeTitle> createState() => _MyLargeTitleState();
}

class _MyLargeTitleState extends State<MyLargeTitle> {
  @override
  void initState() {
    super.initState();
    print('initState!');
  }

  @override
  void dispose() {
    super.dispose();
    print('dispose!');
  }

  @override
  Widget build(BuildContext context) {
    print('build!');
    return Text('My Large Title',
        style: TextStyle(
            fontSize: 30,
            color: Theme.of(context).textTheme.titleLarge?.color));
  }
}

flutter: initState!
flutter: build!

dispose!
// 위젯 트리에서 제거됨

'Frontend > Dart&Flutter' 카테고리의 다른 글

Data Fetching과 패키지 설치  (0) 2023.12.15
플러터 화면구성  (1) 2023.10.31
플러터란? 프로젝트 시작하기  (1) 2023.10.31
Dart 기초 (Functions, Classes)  (0) 2023.08.15
Dart 기초 (Variables, DataType)  (1) 2023.04.21