일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 스택 자바 코드
- 객체
- 클래스
- rxjava cold observable
- ANR이란
- 서비스 vs 쓰레드
- android retrofit login
- rxjava disposable
- 서비스 쓰레드 차이
- 스택 큐 차이
- ar vr 차이
- 안드로이드 유닛 테스트
- 안드로이드 유닛테스트란
- 2022 플러터 설치
- 안드로이드 라이선스 종류
- 자바 다형성
- rxjava hot observable
- 2022 플러터 안드로이드 스튜디오
- android ar 개발
- 멤버변수
- 안드로이드 os 구조
- jvm이란
- 안드로이드 레트로핏 사용법
- jvm 작동 원리
- 플러터 설치 2022
- 안드로이드 유닛 테스트 예시
- 안드로이드 라이선스
- Rxjava Observable
- 큐 자바 코드
- 안드로이드 레트로핏 crud
- Today
- Total
나만을 위한 블로그
[Flutter] Stateless, Stateful 위젯의 생명주기 본문
이 포스팅에선 두 종류 위젯들의 생명주기를 확인한다. 관련 공식문서는 아래를 참고했다.
https://api.flutter.dev/flutter/widgets/State-class.html
https://docs.flutter.dev/ui#responding-to-widget-lifecycle-events
먼저 Stateless 위젯이다. 상태 비저장 위젯이라고도 하는 이 위젯은 build()라는 하나의 생명주기 함수만 갖고 있다.
Stateless 위젯은 생성된 후 내부 상태가 변하지 않기 때문에 사용자에게 UI를 렌더링해서 표시하는 build() 하나만 갖고 있는 것이다.
다음은 Stateful 위젯이다. 상태를 다루는 위젯인 만큼 여러 생명주기 함수를 갖고 있다.
아래는 Stateful 위젯의 생명주기 함수들의 호출 순서를 도식화한 그림이다.
createState
가장 먼저 호출되는 메서드는 createState()인 걸 볼 수 있다. 플러터 프레임워크가 위젯의 State 인스턴스를 만들어 리턴하는 역할을 하기 때문에 이 메서드는 반드시 존재해야 한다.
플러터로 UI를 만들어 봤다면 이 함수는 이미 본 적이 있다. Stateful 위젯을 만들 때 자동완성되는 코드들 중 createState()를 호출하는 부분이 있다.
class MyApp extends StatefulWidget {
@override
State<MyApp> createState() => _MyAppState();
}
MyApp은 Stateful 위젯으로 선언되서 상태를 스스로 관리할 수 있고, createState()는 위젯 상태를 저장하는 _MyAppState 인스턴스를 리턴한다.
공식문서에선 뭐라고 설명하는지 확인한다.
https://api.flutter.dev/flutter/widgets/StatefulWidget/createState.html
트리의 지정된 위치에 이 위젯에 대한 변경 가능한 상태를 만든다. 서브클래스는 이 메서드를 재정의해서 관련된 State 서브클래스의 새로 생성된 인스턴스를 리턴해야 한다
프레임워크(플러터)는 Stateful 위젯의 생명주기 동안 이 메서드를 여러 번 호출할 수 있다. 위젯이 트리의 여러 위치에 삽입된 경우 프레임워크는 각 위치에 별도의 State 객체를 만든다. 마찬가지로 위젯이 트리에서 제거됐다가 나중에 트리에 다시 삽입되면 프레임워크는 createState()를 재호출해서 새 State 객체를 만들어 State 객체의 생명주기를 간소화한다
initState
위젯 트리에 삽입될 때 initState()가 한 번 호출된다. 위젯이 만들어져서 위젯 트리에 처음 삽입될 때 클래스 생성자가 호출된 후 호출되기 때문에 컨트롤러, 리스너 설정, 초기 데이터 불러오기 같은 1회성 초기화 또는 애니메이션 시작, 객체 생성, 구독을 설정하는 등 상태 변수를 초기화하거나 설정할 때 사용한다.
https://api.flutter.dev/flutter/widgets/State/initState.html
객체가 트리에 삽입될 때 호출된다. 프레임워크는 생성하는 각 State 객체에 대해 이 메서드를 한 번 호출한다
이 객체가 트리에 삽입된 위치(즉 컨텍스트) 또는 이 객체 구성에 사용된 위젯, State의 build()가 자체적으로 상태를 바꿀 수 있는 객체(ChangeNotifier, Stream)나 알림을 수신하기 위해 구독할 수 있는 다른 객체에 의존하는 경우 initState, didUpdateWidget, dispose에서 제대로 구독, 구독 취소를 수행해야 한다
- initState에서 객체를 구독
- 업데이트된 위젯 구성으로 인해 객체를 바꿔야 하는 경우 didUpdateWidget에서 이전 객체의 구독을 취소하고 새 객체를 구독
- dispose에서 객체 구독 취소
이 메서드에서 BuildContext.dependOnInheritedWidgetOfExactType을 사용하면 안 된다. 그러나 이 메서드 바로 다음에 didChangeDependencies가 호출되고, 여기서 BuildContext.dependOnInheritedWidgetOfExactType을 쓸 수 있다. 이 메서드의 구현은 super.initState() 같이 상속된 메서드를 호출하는 것으로 시작해야 한다
super.initState()는 initState()를 자동완성으로 구현한다면 반드시 생기기 때문에 이걸 지우지 않기만 하면 된다.
참고로 핫 리로드를 수행하면 reassemble()이 호출되는데 이 메서드는 initState()를 호출해서 모든 데이터를 초기화한다. reassemble()의 문서는 아래를 참고한다.
https://api.flutter.dev/flutter/widgets/State/reassemble.html
didChangeDependencies
https://api.flutter.dev/flutter/widgets/State/didChangeDependencies.html
State 객체의 종속성이 바뀔 때 호출된다. 이전에 빌드 호출이 나중에 변경된 inheritedWidget을 참조한 경우 프레임워크는 이 메서드를 호출해서 이 객체에 변경사항을 알린다. 이 메서드는 initState 직후 호출되기도 한다
이 메서드에서 BuildContext.dependOnInheritedWidgetOfExactType을 호출하는 게 안전하다. 프레임워크는 종속성이 바뀐 후에 항상 빌드를 호출하므로 서브클래스가 이 메서드를 재정의하는 경우는 거의 없다
일부 서브클래스가 이 메서드를 재정의하는 이유는 종속성이 바뀔 때 네트워크 가져오기 등 비용이 많이 드는 작업을 수행해야 하는데, 이 작업을 모든 빌드에 수행하기엔 비용이 너무 많이 들기 때문이다
이 메서드는 initState() 직후에 바로 호출되는 메서드로 위젯이 의존하는 데이터를 가진 객체가 호출될 때마다 호출된다.
업데이트되는 위젯을 상속한 경우, 비용이 많이 드는 State 객체의 종속성이 변경될 때, 네트워크 호출이 필요한 때 유용하다.
build
https://api.flutter.dev/flutter/widgets/State/build.html
이 위젯으로 표시되는 UI 일부를 설명한다. 프레임워크는 여러 상황에서 이 메서드를 호출한다
- initState 호출 후
- didUpdateWidget 호출 후
- setState 호출을 받은 후
- State 객체의 종속성이 바뀐 후(이전 빌드에서 참조한 InheritedWidget이 바뀌는 등)
- deactive 호출 후 다른 위치의 트리에 State 객체를 다시 삽입하는 경우
이 메서드는 잠재적으로 모든 프레임에서 호출될 수 있으며 위젯 빌드 이상의 사이드 이펙트가 없어야 한다. 프레임워크는 이 메서드가 리턴한 위젯이 기존 서브트리의 루트를 업데이트할 수 있는지 여부에 따라 기존 서브트리를 업데이트하거나 서브트리를 제거하고 새 서브트리를 부풀려서 이 위젯 아래의 서브트리를 위젯으로 대체한다(위젯 호출에 따라 결정됨)
일반적으로 구현은 이 위젯의 생성자, 지정된 BuildContext 및 State 객체 내부 상태의 정보로 구성된 새로 생성된 위젯 배열을 리턴한다. 지정된 BuildContext에는 이 위젯이 빌드되는 트리 위치 정보가 포함된다...(중략)
여기서 내가 작성한 위젯들의 빌드가 이뤄진다. 이 메서드는 위젯을 렌더링해야 할 때마다 호출되고 이 안에서 State 변수에 접근, 업데이트해서 UI를 동적으로 변경하거나 화면에 표현하는 처리가 이뤄진다.
didUpdateWidget
https://api.flutter.dev/flutter/widgets/State/didUpdateWidget.html
위젯 구성이 바뀔 때마다 호출된다. 부모 위젯이 재빌드되고 트리의 이 위치가 동일한 런타임 유형, Widget.Key를 가진 새 위젯을 표시하도록 요청하면 프레임워크는 새 위젯을 참조하도록 State 객체의 위젯 속성을 업데이트한 후 이전 위젯을 인수로 써서 이 메서드를 호출한다
위젯이 바뀔 때 응답하려면(암시적 애니메이션 시작 등) 이 메서드를 재정의한다. 프레임워크는 항상 이 메서드를 호출한 후 build()를 호출하므로 이 메서드에서 setState()를 호출하는 건 중복된다. build()가 자체적으로 상태를 바꿀 수 있는 객체(ChangeNotifier, Stream, 알림을 수신하도록 구독할 수 있는 다른 객체 등)에 의존한다면 initState, didUpdateWidget, dispose에서 제대로 구독 / 구독 취소를 해야 한다
- initState에서 객체 구독
- 업데이트된 위젯 구성으로 인해 객체를 바꿔야 한다면 didUpdateWidget에서 이전 객체의 구독을 취소하고 새 객체 구독
- dispose에서 객체 구독 취소
위젯의 부모가 바뀌면(버튼의 글자, 색 변경 등) 이 메서드를 통해 알림을 받는다. 이후 업데이트에 따라 모양을 바꿀 수 있다.
즉 위젯의 구성이 변경될 때(부모가 새 데이터를 전달할 때 등) 호출된다고 볼 수 있고, 이전 상태와 비교하기 위해 위젯 인스턴스를 매개변수로 받는다. 상태를 업데이트하거나 구성 변경에 따른 작업을 수행할 때 사용한다.
setState
https://api.flutter.dev/flutter/widgets/State/setState.html
객체의 내부 상태가 바뀌었음을 프레임워크에 알린다. State 객체의 내부 상태를 바꿀 때마다 setState에 전달하는 함수에서 변경을 수행한다.
setState(() { _myState = newValue; });
제공된 콜백은 즉시 동기적으로 호출된다. 실제로 상태가 언제 설정됐는지 불분명하므로 콜백은 Future를 리턴하면 안 된다. 콜백은 비동기일 수 없다. setState()를 호출하면 이 서브트리의 UI에 영향을 줄 수 있는 방식으로 이 객체의 내부 상태가 바뀌었음을 프레임워크에 알리고, 프레임워크는 State 객체에 대한 빌드를 예약하게 된다
이 메서드 없이 상태를 직접 바꾸면 프레임워크가 빌드를 예약하지 않고 서브트리의 UI가 새 상태를 반영하도록 업데이트되지 않을 수 있다. 일반적으로 이 메서드는 변경과 연관될 수 있는 계산이 아닌 상태의 실제 변경을 래핑할 때만 쓰는 게 좋다...(중략)
이 메서드는 위젯의 내부 상태가 바뀌어서 업데이트해서 최신 상태를 보여줘야 한다고 프레임워크에 알리기 위해 사용한다. 상태를 바꿀 때 이 메서드를 쓰지 않으면 값은 변하더라도 UI는 최신화되지 않으니 적절하게 사용해야 한다.
deactivate
https://api.flutter.dev/flutter/widgets/State/deactivate.html
객체가 트리에서 제거될 때 호출된다. 프레임워크는 이 상태 객체를 트리에서 제거할 때마다 이 메서드를 다시 호출한다. 경우에 따라 프레임워크는 State 객체를 트리의 다른 곳에 다시 삽입한다. 이 경우 프레임워크는 activate()를 호출해 상태 객체가 비활성화될 때 해제했던 리소스를 다시 얻을 기회를 준다. 그 다음 State 객체가 트리의 새 위치에 적응할 기회를 주기 위해 build()도 호출한다
프레임워크가 서브트리를 다시 삽입하는 경우 서브트리가 트리에서 제거된 애니메이션 프레임이 끝나기 전에 삽입한다. 따라서 State 객체는 프레임워크가 dispose()를 호출할 때까지 대부분의 리소스 해제를 연기할 수 있다. 서브클래스는 이 메서드를 재정의해서 이 객체와 트리의 다른 요소 간 링크를 정리해야 한다. 이 메서드의 구현은 super.deactivate() 같이 상속된 메서드 호출로 끝나야 한다
위젯이 스크롤 등의 이유로 화면에 더 이상 표시되지 않으면(트리에서 일시적으로 제거될 때) 이 메서드가 호출된다. 여기선 애니메이션의 일시 정지나 나중에 복원할 수 있도록 상태를 저장하는 등의 정리 작업을 수행할 수 있다.
dispose
https://api.flutter.dev/flutter/widgets/State/dispose.html
객체가 트리에서 영구적으로 제거될 때 호출된다. 프레임워크는 이 State 객체가 다시 빌드되지 않을 때 이 메서드를 호출한다. dispose()가 호출되면 State 객체는 마운트 해제된 것으로 간주되고, 마운트된 프로퍼티는 false가 된다. 이 시점에서 setState()를 호출하는 것은 오류다. 생명주기에서 이 단계는 종료 단계이므로 폐기된 State 객체를 다시 마운트할 순 없다. 서브클래스는 이 메서드를 재정의해서 이 객체가 가진 모든 리소스를 해제해야 한다(활성 애니메이션 중지 등)
State의 build()가 ChangeNotifier 또는 Stream 같이 상태를 바꿀 수 있는 객체 또는 알림을 수신하도록 구독할 수 있는 다른 객체에 의존하는 경우 initState, didUpdateWidget, dispose에서 제대로 구독 / 구독 취소를 수행해야 한다. 이 메서드의 구현은 super.dispose() 같이 상속된 메서드 호출로 끝나야 한다...(중략)...앱이 언제 종료될지 예측할 순 없다. 배터리에 불이 나거나 기기를 수영장에 떨어뜨리거나 OS가 메모리 압박 때문에 일방적으로 종료할 수 있따. 앱은 이런 예기치 않은 갑작스런 종료에도 제대로 작동해야 할 책임이 있다. 전체 위젯 트리를 인위적으로 폐기하려면 Sizedbox.shrink 등의 위젯으로 runApp()을 호출하라. 플랫폼 종료 메시지를 수신하려면 AppLifecycleListener API 사용을 고려하라
위젯의 생명주기에서 마지막에 호출되는 메서드다. 위젯이 위젯 트리에서 영구적으로 제거되면 이 메서드가 호출되며 갖고 있던 모든 리소스를 해제할 수 있다.
참고한 사이트)
https://www.dhiwise.com/post/unveiling-the-flutter-widget-lifecycle
'Flutter' 카테고리의 다른 글
[Flutter] AppBar란? AppBar 사용법 (0) | 2024.08.06 |
---|---|
[Dart] 반복문 (0) | 2024.08.06 |
[Flutter] Future, async / await를 통한 비동기 처리 방법 (0) | 2024.07.24 |
[Dart] final vs const (0) | 2024.07.22 |
[Dart] 생성자(constructor) 알아보기 (0) | 2024.07.21 |