| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 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 |
- 자바 출력 방식
- 자바 포맷 출력
- npm
- DART
- ListView
- Flutter
- develop
- abap
- java 콘솔 출력 차이
- printf
- java 출력
- 배포
- lifecycle
- Clean Architecture
- nodejs
- java
- JS
- println
- react
- UI/UX
- riverpod
- 엡
- firebase
- LLM
- JQ
- 단축키
- unity
- 앱심사
- scss
- 자바스크립트
- Today
- Total
guricode
[Flutter] StatefulWidget의 생명주기(Lifecycle) 본문
StatefulWidget은 State를 가지는 위젯으로 상태를 관리할수있다.
StatefulWidget에는 라이프사이클이 존재하는데 이 라이프사이클을 알고있어야 앱에서 상태관리에 유용하게 사용할수있다.
1.createState() - State 객체 생성
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
// StatefulWidget이 생성될 때 단 한 번만 호출
// State 객체를 생성하여 반환
}
state객체를 생성해준다. 처음 위젯이 삽입될때만 호출된다.
2.initState()-초기화
@override
void initState() {
super.initState(); // 반드시 가장 먼저 호출!
// 사용 예시:
_controller = AnimationController(vsync: this);
_scrollController = ScrollController();
_fetchData(); // API 호출
_timer = Timer.periodic(Duration(seconds: 1), (timer) {
// 주기적 작업
});
}
State객체가 생성된 직후 한번 호출된다.
build()보다 먼저 실행 된다.
주요역할은 컨트롤러 초기화,리스너등록, 초기데이터 로드, Stream 구독시작, 타이머설정 등이다.
context를 사용할 수 있지만, Theme.of(context) 같은 InheritedWidget은 아직 사용 불가하다. InheritedWidget는 didChangeDependencies단계에서 의존성 설정이 완료되기 때문이다. didChangeDependencies 여기서 Theme, MediaQuery 등 사용 가능하다.
따라서 아래와 같이 시도하면 오류가 난다.
// ❌ 잘못된 예
@override
void initState() {
super.initState();
final size = MediaQuery.of(context).size; // 에러 발생 가능
}
// ✅ 올바른 예 - didChangeDependencies 사용
@override
void didChangeDependencies() {
super.didChangeDependencies();
final size = MediaQuery.of(context).size;
}
1. createState()
→ State 객체 생성
→ context 생성 ✅
→ 하지만 위젯 트리에 아직 안 붙음
2. initState()
→ context는 있지만 ⚠️
→ 위젯 트리와의 "의존성 관계" 설정 전
→ InheritedWidget을 찾을 수 없음 ❌
3. didChangeDependencies()
→ 위젯 트리에 완전히 연결됨 ✅
→ InheritedWidget 의존성 설정 완료 ✅
→ 이제 Theme, MediaQuery 등 사용 가능!
4. build()
→ 모든 것 사용 가능 ✅
3.didChageDependencies()-의존성 변경
@override
void didChangeDependencies() {
super.didChangeDependencies();
// 사용 예시:
final theme = Theme.of(context); // 이제 사용 가능!
final mediaQuery = MediaQuery.of(context);
final locale = Localizations.localeOf(context);
// 의존성이 변경될 때마다 실행할 코드
_updateThemeBasedData(theme);
}
initState() 직후 처음 한번 실행된다.
이후 InheritedWidget이 변경될때마다 실행된다.
주로 InheritedWidget(Theme, MediaQuery 등)에 의존하는 초기화를 한다.
자주호출될수있으니 무거운 작업은 피해야한다.
그리고 initState()와 달리 여러번 호출 가능하다.
4.Build() - UI구성
@override
Widget build(BuildContext context) {
print('build 호출됨: ${DateTime.now()}');
return Scaffold(
appBar: AppBar(title: Text('Life Cycle')),
body: Column(
children: [
Text('카운터: $_counter'),
ElevatedButton(
onPressed: () => setState(() => _counter++),
child: Text('증가'),
),
],
),
);
}
didChangeDependencies() 이후 처음 한 번, didUpdateWidget() 이후 ,부모위젯이 재빌드될때 실행된다.
그리고 상태 setState가 호출된때마다 리빌드된다.
build안에서는 상태변경(setState) 금지다.
빌드안에서 또 setState가 실행되면 중복으로 실행되게된다.
5.didUpdatewidget()-위젯 업데이트
@override
void didUpdateWidget(MyWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// 사용 예시:
if (widget.title != oldWidget.title) {
print('제목이 변경됨: ${oldWidget.title} -> ${widget.title}');
_updateTitle();
}
if (widget.url != oldWidget.url) {
_fetchNewData(widget.url);
}
}
부모위젯이 재빌드되어 같은 타입의 새로운 위젯으로 교체될때 실행된다.
이후 자동으로 build()가 실행된다.
이 부분이 이해가 잘 안갔는데 아래 예시코드로 쉽게 확인할수있었다.
예시 코드
class NetworkImageWidget extends StatefulWidget {
final String imageUrl;
NetworkImageWidget({required this.imageUrl});
@override
_NetworkImageWidgetState createState() => _NetworkImageWidgetState();
}
class _NetworkImageWidgetState extends State<NetworkImageWidget> {
late Future<Uint8List> _imageFuture;
@override
void initState() {
super.initState();
_imageFuture = _loadImage(widget.imageUrl);
}
@override
void didUpdateWidget(NetworkImageWidget oldWidget) {
super.didUpdateWidget(oldWidget);
// URL이 변경되었을 때만 새로 로드
if (oldWidget.imageUrl != widget.imageUrl) {
print('이미지 URL 변경: ${oldWidget.imageUrl} → ${widget.imageUrl}');
_imageFuture = _loadImage(widget.imageUrl);
}
}
Future<Uint8List> _loadImage(String url) async {
// 이미지 로드 로직
return Uint8List(0);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<Uint8List>(
future: _imageFuture,
builder: (context, snapshot) {
// UI 구성
return Container();
},
);
}
}
6.setState() - 상태변경 알림
void _incrementCounter() {
setState(() {
_counter++; // 상태 변경
_message = '카운터가 증가했습니다';
});
// setState가 완료된 후에 실행됨
print('setState 완료');
}
// 비동기 작업 후 상태 변경
Future<void> _loadData() async {
final data = await fetchDataFromServer();
// mounted 체크 필수!
if (mounted) {
setState(() {
_data = data;
});
}
}
생명주기 메서드는 아니지만 중요해서 넣었다.
다음 프레임에 build()재호출을 예약한다.
initState(), dispose() 내에서 호출 불가능하다.
비동기 작업 후에는 mounted체크를 습관적으로 해줘야한다.
7.deactivate() - 위젯트리에서 제거
@override
void deactivate() {
print('deactivate 호출 - 위젯이 트리에서 제거됨');
super.deactivate();
// 임시 제거 시 정리할 작업
}
위젯이 트리에서 제거될떄 실행된다.
dispose바로 전에 실행된다.
드물게 사용되며 위젯이 다른위치로 이동할때 처리한다.
8.dispose() - 정리 및 해제
@override
void dispose() {
// 리소스 정리 (반드시 super.dispose() 전에!)
_controller?.dispose();
_scrollController?.dispose();
_focusNode?.dispose();
_timer?.cancel();
_subscription?.cancel();
print('dispose 호출 - State 객체 영구 제거');
super.dispose(); // 반드시 마지막에 호출!
}
State가 영구적으로 제거될때, deactivate() 이후 실행된다.
주로 컨트롤러 dispose,리스너제거,Stream 구독취소,메모리 누수 방지로 사용한다.
| 메서드명 | 호출시점 | 호출 횟수 | BuildContext 접근 | setState 사용 | 주요 역할 | Build 호출 여부 |
| createState() | Widget 생성 시 | 1회 | ❌ | ❌ | State 객체 생성 | ❌ |
| initState() | State 생성 직후 | 1회 | ⚠️ 제한적 | ❌ | 초기화, 리스너 등록, API 호출 | ❌ |
| didChangeDependencies() | initState 직후, InheritedWidget 변경 시 | 1회 이상 | ✅ | ✅ | InheritedWidget 의존성 처리 | ✅ |
| build() | 위 단계 후, setState() 호출 시 | 매우 많음 | ✅ | ✅ | UI 위젯 트리 반환 | ❌ |
| didUpdateWidget() | 부모 재빌드로 새 위젯 받을 때 | 0회 이상 | ✅ | ✅ | 속성 변경 감지 및 처리 | ✅ |
| deactivate() | 위젯 트리에서 제거 시 | 0-1회 | ✅ | ✅ | 임시 제거 처리 (드물게 사용) | ⚠️ |
| dispose() | State 영구 제거 시 | 1회 | ✅ | ❌ | 리소스 정리, 메모리 해제 | ❌ |
| 항목 | StatelessWidget | StatefulWidget |
| 상태 보관 | 없음(불변). 생성자 값만 표시 | 있음(가변). State 객체에 저장 |
| 재빌드 트리거 | 부모가 새 값 전달 시만 | setState() 호출 시 해당 위젯 subtree 리빌드 |
| 라이프사이클 | build 한 번 중심 | initState → build → didUpdateWidget → dispose 등 |
| 사용 예 | 아이콘, 고정 텍스트, 단순 레이아웃 | 토글, 폼 입력, 애니메이션, 스크롤 위치 등 |
'앱 > Flutter&Dart' 카테고리의 다른 글
| [Flutter] 상수 클래스 (0) | 2025.10.20 |
|---|---|
| [Flutter]ListView, ListView.builder, SingleChildScrollView + Column 차이점 (0) | 2025.10.19 |
| [Flutter] Flutter 앱에서 상태(state)란 무엇인가 (0) | 2025.10.19 |
| [자취의 정석] 자취도우미 ai 챗봇 만들기, XOR, Object 키워드, Entity -클린 아키텍쳐 Domain 레이어 (0) | 2025.10.14 |
| [Dart] 자주쓰는 Object 키워드 (예약어) (0) | 2025.10.14 |