Study/Dart,Flutter

7. StatefulWidget의 LifeCycle(생명주기) 그리고 setState

코딩 잘 할거얌:) 2021. 8. 30. 21:01
반응형

이전 포스팅은 state와 widget 그리고 statelessWidget에 대해서 알아보았다. 이번에는 statefulWidget과 상태 관리(state management)에 관해서 알아보도록 하자. 오늘도 어김없이 이론이다. 하지만 이번에는 굉장히 중요한 내용이니 반드시 읽고 넘어가기 바란다.

dㅏ 벌써부터 머리가아파요!

 


statefulWidget은 무엇일까?

statefulWidget은 stateless와 다르게 상태를 변하게 할 수 있는 위젯이다. 앞서 포스팅에서 말한 것처럼 플러터에서 상태라는 것은 애플리케이션에 영향을 주는 데이터이다. 그래서 애플리케이션의 데이터가 변경이 된다면 그에 맞게 화면이 바뀌어야 한다. 유저가 버튼을 클릭하면 스낵바가 올라온다던가 하는 행동이다. 그렇다면 statefulWidget으로 애플리케이션을 어떻게 데이터(상태)를 관리하고 화면을 변경하는 것일까?

statefulWidget의 생명주기

statefulWidget의 생명주기는 다음과 같다.

출처 : https://betterprogramming.pub/stateful-widget-lifecycle-a01c44dc89b0

위의 그림을 잘 살펴보자. 애플리케이션에서 작동하는 메커니즘과 순서를 생명주기라고 한다. 시작부터 천천히 예시 코드와 함께 보자.

 

createState

state를 생성하는 곳이다. statefulWidget에서 state를 실행시킨다.

  @override
  _MyHomePageState createState() => _MyHomePageState();

 

mounted

위의 사진에는 없지만 mounted라는 것이 true로 변하게 된다. 실행하고 있는 페이지가 띄워져 있는지 확인하는 전역 변수이다. mounted는 특정 페이지에서 애플리케이션에서 애니메이션이나 타이머를 실행시키는 경우에 mounted 된 후 사용을 한다. 플러터에 Rive 혹은 애니메이션을 적용시킨다면 유용하게 사용할 수 있다.

  @override
  bool get mounted => super.mounted;

 

initState

state를 초기화하는 함수이다. initState는 statefulWidget이 실행하면 단 한 번만 작동을 한다. 그 이후 작동시키기 위해서는 해당 statefulWidget을 종료한 후에 가능하다. 이 함수 안을 수정하고 hot reload 또는 rebuild를 아무리 하더라도 바뀌지 않으니 반드시 수정한 statefulWidget을 종료(dispose) 후 다시 실행시켜야 한다. 이 함수에서 controller를 설정하거나 listener를 설정을 한다.

 @override
 void initState() {
   scrollController.addListener(() {
     if (scrollController.offset >= 600) {
       if (!_isVisibility) {
         _isVisibility = true;
       }
     } else if (scrollController.offset < 600) {
       if (_isVisibility) {
         _isVisibility = false;
       }
     }
   });
   super.initState();
 }

 

didChangeDependencies

메서드는 위젯이 최초 생성될 때initState 다음에 바로 호출된다. 위젯이 의존하는 데이터의 객체가 호출될 때마다 호출된다.라고 공식문서에 적혀있는데, 정확하게 어떻게 사용하는지는 잘 모르겠다. 다만 사용하는 곳이 없냐, 는 아니다. build가 실행되기 전, Buildcontext를 사용하고 싶을 때 didChangeDependencies에서 사용하면 된다. initState에서는 context가 형성되기 전에 실행이 되므로 안 되는 경우가 종종 발생한다.

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
  }

 

build

화면의 Widget을 그리기 시작하는 곳이다. 상태가 변경이 되어 앞에 나올 setState를 실행하여 다시 그릴 때도 호출이 되며, 플러터 작성하고 hot reload 할 때도 다시 그리게 된다. 

  class statefulWidgetExample extends StatefulWidget {
    statefulWidgetExample({Key? key}) : super(key: key);

    @override
    _statefulWidgetExampleState createState() => _statefulWidgetExampleState();
  }

  class _statefulWidgetExampleState extends State<statefulWidgetExample> {
    @override
    Widget build(BuildContext context) {
      return Container(
         child: null,
      );
    }
  }

setState

setState는 상태(데이터)가 변경이 되었을 때 사용을 한다. setState호출 후, build가 다시 실행이 된다. build가 다시 실행되는 것이기 때문에 화면의 변화가 생겼을 때 사용을 자주 하게 된다. 하지만 build가 전부 새로 변화하기 때문에 모든 위젯이 바뀐다. Futurebuilder 같은 데이터를 불러오는 것조차 예외 없이 다시 다 불러오게 된다. 따라서 상태 관리를 배우게 된다면 위젯 단위로 관리를 하게 되며, setState는 최대한 지양하고, statelessWidget을 사용하게 된다.

floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _counter += 1;
          });
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),

 

didUpdateWidget 

부모 위젯이 재 빌드되어서 위젯이 갱신이 될 때 호출이 된다. setState와 비슷한 기능이다.(사실 어떻게 쓰는지 잘 모른다. 알게 되면 추후 업데이트하도록 하겠다...)

  @override
  void didUpdateWidget(covariant MyHomePage oldWidget) {
    super.didUpdateWidget(oldWidget);
  }

 

dispose

화면이 종료될 때 호출이 된다. 상태도 제거가 되며, 일반적으로 controller를 종료할 때 사용을 한다. 만약 이 함수 안에서 context가 포함되는 것을 실행한다면 statefulWidget의 state와 context가 제거되는 시점에 실행이 되어서 종종 mounted error가 뜬다. 그러니 피하도록 하자.

  @override
  void dispose() {
    textEditingController.dispose();
    scrollController.dispose();
    super.dispose();
  }

 


statefulWidget의 Lifecycle을 살펴보았다. 다음에는 상태 관리 그리고 Riverpod에 대해서 포스팅해보겠다.

오류, 지적사항 그리고 궁금한 것은 댓글 부탁드립니다.

728x90