티스토리 뷰

들어가며

본 게시글은 노마드코더의 Flutter로 웹툰 만들기를 보고 정리한 글입니다.

 

Stateful Widget

이전에 만들었던 UI는 Stateless Widget으로 정적이었다. 아무런 데이터를 저장하지 않고 단지 보여주기만 한다. Stateful Widget은 상태에 따라 변하게 될 데이터를 고려하여 UI에 반영할 수 있다. 위젯에 데이터를 저장하거나 실시간 데이터 변화를 볼 수 있다.

 

Stateful Widget은 state가 없는 위젯 그 자체와 state, 두 개의 부분으로 나뉘어진다. state는 위젯에 들어갈 데이터와 UI를 가진다.

 

코드로 살펴보면 위에는 위젯 그 자체가 있다. State 하나만 가지고 있는 작은 코드이다.

아래는 State가 있다. State는 UI를 구축하는 곳이다. State의 데이터를 바꿀때마다 UI는 새로고침되면서 최신 데이터를 보여준다. 여기서의 데이터는 클래스 프로퍼티로 표현된다.

 

다음 코드는 버튼이 클릭될 때마다 counter가 올라가는 것이 UI에 표시되는 예제이다. Stateful Widget의 State는 위젯의 데이터와 UI를 저장하고, 데이터가 변경되면 위젯의 UI도 변경된다. 여기서 중요한 것은 setState 함수를 통해서 State에 데이터가 변경된 것을 알려줘야한다. setState 메소드는 build 메소드를 새로운 데이터와 함께 호출하는 것이다. 그렇기에 굳에 setState 안에서 데이터가 변하는 코드를써 주지 않고 변하게 한 다음 setState를 호출해도 마찬가지로 UI는 잘 변한다. 다만 가독성의 측면 때문에 setState 안에 코드를 넣는 것을 추천한다고 한다.

 

※ setState 안에 있는 () {}는 dart의 익명함수라는 문법이다. 그냥 함수 자체를 setState에 넘겨준 것이라고 생각하면 된다. 이는 함수가 dart에선 일급 객체(다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체)이기 때문에 가능하다.

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

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

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,
                ),
              )
            ],
          ),
        ),
      ),
    );
  }
}

 

BuildContent

Flutter에는 매번 글꼴, 폰트 크기, 색상 등 지정해 줄 필요 없이 기본적으로 스타일시트를 지정할 수 있다. 바로 theme이다.

 

initState

State 에는 initState라는 메소드가 있다. 대부분의 경우 property처럼 클래스 안에서 State를 초기화해도 되지만, 부모 변수나 API처럼 다른 곳에서 데이터를 받아와 초기화해야 할 경우에 쓰인다. initState는 최초 1회만 호출되며, build 메소드보다 먼저 실행된다.

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

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

 

dispose

dispose는 위젯이 스크린에서 제거될 때 호출된다. 위젯이 위젯 트리에서 제거되기 전에 무언가를 취소하고 싶을때 사용할 수 있다.

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 to see'),
              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("called initState");
  }

  @override
  void dispose() {
    super.dispose();
    print("called dispose");
  }

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

 

My Large Title에서 버튼을 누를때는 dispose가 호출되고 nothing to see에서 버튼을 누를때는 initState와 build가 호출되는 것을 볼 수 있다.

 

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함