StatefulWidget의 생명주기 메서드

 

createState()

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();  // 가장 먼저 호출
}

 

initState()

class _MyWidgetState extends State<MyWidget> {
  late StreamSubscription _subscription;
  
  @override
  void initState() {
    super.initState();  // 반드시 super 호출
    
    // 초기화 작업 수행
    _subscription = Stream.periodic(Duration(seconds: 1))
        .listen((data) => print('Stream data: $data'));
  }
}

 

didChangeDependencies()

class _MyWidgetState extends State<MyWidget> {
  ThemeData? _theme;
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    
    // 상위 위젯의 데이터 의존성 변경 시 호출
    _theme = Theme.of(context);  // InheritedWidget 데이터 접근
  }
}

 

 

build()

class _MyWidgetState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text('Widget Content'),
    );
  }
}

 

 

didUpdateWidget()

class _MyWidgetState extends State<MyWidget> {
  @override
  void didUpdateWidget(MyWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    
    // 위젯이 업데이트될 때 호출
    if (widget.someProperty != oldWidget.someProperty) {
      // 속성 변경에 따른 처리
    }
  }
}

 

 

dispose()

class _MyWidgetState extends State<MyWidget> {
  late StreamSubscription _subscription;
  
  @override
  void dispose() {
    // 리소스 해제
    _subscription.cancel();
    super.dispose();  // 반드시 마지막에 super 호출
  }
}

 

 

생명주기 단계별 주의사항

 

initState

  • super.initState() 반드시 먼저 호출
  • context 사용 불가 (didChangeDependencies에서 사용)
  • 비동기 작업 시작 가능
  • 한 번만 호출됨

didChangeDependencies

  • InheritedWidget 데이터 접근
  • 여러 번 호출될 수 있음
  • 상위 위젯의 데이터 변경 시 호출

build

  • 순수 함수여야 함
  • 상태 변경 금지 (setState 호출 금지)
  • 가능한 가벼운 연산만 수행

dispose

  • 리소스 해제 필수
  • super.dispose() 마지막에 호출
  • mounted 체크 후 setState 호출

 

일반적인 실수와 해결방법

 

메모리 누수 방지

class _MyWidgetState extends State<MyWidget> {
  StreamSubscription? _subscription;
  
  @override
  void initState() {
    super.initState();
    _subscription = Stream.periodic(Duration(seconds: 1))
        .listen((data) {
          if (!mounted) return;  // mounted 체크
          setState(() {
            // 상태 업데이트
          });
        });
  }
  
  @override
  void dispose() {
    _subscription?.cancel();
    super.dispose();
  }
}

 

비동기 작업 처리

class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    _loadData();
  }
  
  Future<void> _loadData() async {
    try {
      await Future.delayed(Duration(seconds: 2));
      if (!mounted) return;
      setState(() {
        // 상태 업데이트
      });
    } catch (e) {
      print('Error: $e');
    }
  }
}

 

 

Context 사용

class _MyWidgetState extends State<MyWidget> {
  @override
  void initState() {
    super.initState();
    // Error: context 사용 불가
    // Theme.of(context);
  }
  
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // OK: context 사용 가능
    final theme = Theme.of(context);
  }
}

 

 

마치며

위젯의 생명주기를 잘 이해하고 적절히 활용하면 메모리 누수를 방지하고 효율적인 상태 관리가 가능합니다. 특히 dispose() 메서드에서의 리소스 해제와 비동기 작업 시의 mounted 체크는 매우 중요합니다.

'Language > Flutter' 카테고리의 다른 글

[Flutter] 앱 아이콘 변경  (0) 2024.11.22
[Flutter] 앱 이름 변경  (0) 2024.11.21
[Dart] fold() 메소드  (0) 2024.11.19
[Flutter] 상태 관리 기초 - setState  (0) 2024.11.18
[Dart] Spread 연산자(...)  (0) 2024.11.17

+ Recent posts