[Flutter] State(상태)를 밥아저씨가 설명하는 것처럼 쉽게 알아보기

Hans Park·2022년 1월 14일
2

Flutter

목록 보기
7/14
post-thumbnail

📈 상태

State

공식문서에 의하면 state란,

  1. 위젯이 빌드될 때 동기적으로 읽을 수 있는 정보
  2. 위젯의 생명주기가 끝나기 전까지 변경될 수 있는 정보

라고 말하고 있다.

다시말해, 지금 isLoading은 False이네? 혹은 color의 값은 Colors.red이네? 라고 볼 수 있을 것이다.

App State, Widget State

State는 두개로 나눌 수 있다.

App state는 플러터 앱 전반에 걸쳐 사용되는 data로, 여러 위젯이나 스크린(얘도 위젯이긴 하지...)에서 쓰이는 것들을 의미한다.

  • 로그인 정보
  • 유저설정
  • 장바구니

등이 있겠다.

Widget state는 Ephemeral state, local state라고도 불리며, 위젯 내부에서만 사용되는 data를 말한다.
위젯 내부에서 사용되니 딱히 공유하거나 반환할 필요가 없다.





하지만, 이 두 상태를 명확히 구분할 순 없다.
여러 개발 방법론이나 컨벤션, 개발팀의 개발자들의 약속처럼 사전에 약속하는 방법밖에 없겠다.

Stateless, Stateful

알다시피, 휴대폰 스크린에 위젯을 띄우기 위해선 stateless 혹은 stateful 클래스를 만들어야 한다.

안드로이드 스튜디오에서 stless, stful을 타이핑한 후 자동완성을 클릭하면 클래스를 자동으로 만들어준다.
이제 자동완성 없이 살 수 없어진 몸.....

Scaffold이야기는 잠시 빼도록 하자.

사진 속 두 위젯의 공통적인 부분 중 하나인 Input Data는 무엇일까?
Text("Hello")에서 Hello라는 String이 Text위젯의 input data가 된다.

즉, 클래스가 받는 매개변수가 되겠다.





StatelessWidget

처음 플러터를 배우면서 가장 헷갈리는 부분이었다.

Stateless는 State가 없는 Widget인가?

답은 아니다! 이다.

Stateless는 내부적으로 상태를 가지고는 있으나,
값을 변경할 수 없기에, 즉 Data의 상태 가 변하지 않기에 Less가 붙었다.

내부적으로 Stateless도 상태를 가지고 있기에,
Input Data가 바뀌면 StatelessWidget도 재빌드(re-rendering)된다.

StatefulWidget

Stateless와 반대로 상태가 존재하는 Widget으로, 내부적으로 data(상태)가 변경되면 그에 맞게 화면을 다시 그려 변경된 부분을 위젯에 반영하게 된다.

StatefulWidget(자세히 말하면 State 객체)은 아래의 생명주기를 따른다.

initState()

위젯이 생성된 후 State 객체가 생성될 때 호출되는 메소드이다.
처음 한번만 호출되고 그 이후론 호출되지 않는다.

didChangeDependencies()

initState가 끝나고 호출되는 함수이며,
해당 위젯이 의존하는 위젯이 변경되면 재호출된다.
(cf. inheritedWidget)

context를 사용하여 변수를 초기화하는 명령어가 있다고 할때,
initState는 context가 형성되기 전 호출되어 사용하지 못하므로,
didChangeDependencies 메소드 내에서 context를 사용하여 변수를 초기화할 수 있다.

build()

이 메소드를 통해 위젯이 그려진다.
State클래스에서 반드시 오버라이딩되야 하며,
상태가 변경될때마다 호출된다.

setState()

State객체의 상태가 변경되었다는 것을 프레임워크에 알리는 메소드이다.
State객체의 상태가 변경되었을 때마다 호출해주어야 한다.

setState를 할때마다 어떠한 명령어를 내리고 싶다면,
build메소드처럼 오버라이딩한 함수를 객체 내에 정의하면 된다.

이때, super.setState(fn)을 하지 않으면 원하지 않는 결과가 나올 수 있다.

class _MyHomePageState extends State<MyHomePage> {
  String str = "";

  
  void setState(VoidCallback fn) {
    str += "in setState\n";
    super.setState(fn);
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Container(
              color: Colors.red,
              child: Text(str),
            ),
            TextButton(
              onPressed: () {
                str += "in build\n";
                setState(() {});
              },
              child: Text("button"),
            ),
          ],
        ),
      ),
    );
  }
}

didUpdateWidget()

부모위젯이 재 빌드되어 위젯이 갱신될때 호출된다.
이때, 해당 메소드가 호출된 후에는 항상 build()메소드가 호출되므로, setState를 이 메소드 내에서 호출하면 build가 두번 호출되는 것이다.

deactivate()

트리에서 State객체가 제거될 때 호출된다.

프레임워크가 제거된 State객체를 트리의 다른 부분에 다시 삽입하는 경우가 있는데,
이때는 build메소드를 한번 호출한다.

dispose()

트리에서 State객체가 영구적으로 제거될 때 호출된다.
이 메소드 내에서 setState를 호출하면 안된다.

reassemble()

hot reload 실행 시 호출되며, build도 같이 호출된다.

🖊 후기

가끔씩 앱개발을 하면서 "어 왜 위젯을 클래스로 분리하면 자동으로 Stateless로 나눌까?" 혹은 "왜 지금 UI가 바뀌지 않지?", "왜 초기화가 initState에서 되지 않지?" 등을 경험하고 했는데, 역시 기초가 중요하다는 것을 느꼈다.

지금은 여러 블로그들의 도움을 받아 짜집기로 정리했는데, 다음에는 공식문서와 직접 코드를 보여주면서 Deep하게 설명해보아야겠다.

출처

profile
장안동 개발새발

0개의 댓글