카테고리 없음

[Flutter] 위젯 생명주기 이해하기: 타이머 관련 오류 해결 사례

mark340 2024. 7. 3. 13:41

1. 소개

Flutter 앱 개발에서 위젯의 생명주기를 이해하는 것은 매우 중요합니다. 이는 앱의 성능을 최적화하고, 메모리 누수를 방지하며, 예기치 않은 오류를 피하는 데 핵심적인 역할을 합니다. 이 포스트에서는 실제 스톱워치 앱 개발 중 발생한 타이머 관련 오류를 통해 Flutter 위젯의 생명주기를 심도 있게 살펴보겠습니다.

2. Flutter 위젯 생명주기 개요

Flutter에는 두 가지 주요 위젯 유형이 있습니다: StatelessWidget과 StatefulWidget. StatefulWidget의 경우, 다음과 같은 주요 생명주기 메서드가 있습니다:

  • initState(): 위젯이 생성될 때 호출됩니다.
  • build(): 위젯을 렌더링할 때 호출됩니다.
  • dispose(): 위젯이 제거될 때 호출됩니다.

3. 사례 연구: 스톱워치 앱의 타이머 오류

다음과 같은 스톱워치 기능을 구현한 SportsModeDetailScreen 위젯이 있었습니다:

class SportsModeDetailScreen extends StatefulWidget {
  @override
  State<SportsModeDetailScreen> createState() => _SportsModeDetailScreenState();
}

class _SportsModeDetailScreenState extends State<SportsModeDetailScreen> {
  Stopwatch _stopwatch = Stopwatch();
  Timer? _timer;
  String _stopwatchText = '00:00:00';
  bool _isRunning = false;

  // ... (기타 메서드)

  void _startStopWatch() {
    setState(() {
      _isRunning = true;
    });
    _stopwatch.start();
    _timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
      setState(() {
        _stopwatchText = _getFormattedTime(_stopwatch.elapsedMilliseconds);
      });
    });
  }

  // ... (기타 메서드)
}

이 코드에서, 사용자가 다른 화면으로 이동할 때 다음과 같은 오류가 발생했습니다:

FlutterError (setState() called after dispose(): _SportsModeDetailScreenState#caba4(lifecycle state: defunct, not mounted)
This error happens if you call setState() on a State object for a widget that no longer appears in the widget tree (e.g., whose parent widget no longer includes the widget in its build).
This error can occur when code calls setState() from a timer or an animation callback.

4. 문제의 원인

이 오류의 주요 원인은 두 가지입니다:

  1. 위젯이 dispose된 후에도 타이머가 계속 실행되고 있었습니다.
  2. 타이머 콜백에서 위젯이 이미 제거된 후에도 setState()를 호출하려 했습니다.

5. Flutter 생명주기와 관련된 주의사항

  • setState()는 위젯이 활성 상태일 때만 호출해야 합니다.
  • 타이머, 스트림 구독 등의 리소스는 위젯이 dispose될 때 반드시 정리해야 합니다.

6. 해결 방안

  1. dispose() 메서드에서 타이머를 취소합니다.
  2. 타이머 콜백에서 mounted 속성을 확인합니다.
  3. setState() 호출 전에 위젯이 여전히 활성 상태인지 확인합니다.

7. 개선된 코드 예시

class _SportsModeDetailScreenState extends State<SportsModeDetailScreen> {
  Stopwatch _stopwatch = Stopwatch();
  Timer? _timer;
  String _stopwatchText = '00:00:00';
  bool _isRunning = false;

  @override
  void dispose() {
    _timer?.cancel(); // 타이머를 직접 취소
    _stopwatch.stop(); // 스톱워치 중지
    super.dispose();
  }

  void _startStopWatch() {
    setState(() {
      _isRunning = true;
    });
    _stopwatch.start();
    _timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
      if (mounted) { // 위젯이 여전히 트리에 있는지 확인
        setState(() {
          _stopwatchText = _getFormattedTime(_stopwatch.elapsedMilliseconds);
        });
      } else {
        t.cancel(); // 위젯이 제거되었다면 타이머 취소
      }
    });
  }

  // ... (기타 메서드)
}

8. 추가적인 생명주기 관리 팁

  • FutureBuilderStreamBuilder를 사용하여 비동기 작업을 관리할 수 있습니다.
  • Provider, Riverpod 같은 상태관리 라이브러리를 사용하면 위젯의 생명주기와 상태 업데이트를 더 효과적으로 관리할 수 있습니다.

9. 결론

Flutter 위젯의 생명주기를 이해하고 적절히 관리하는 것은 견고하고 효율적인 앱을 개발하는 데 필수적입니다. 이 사례 연구를 통해 우리는 타이머 관련 오류를 해결하고, 더 나아가 Flutter의 위젯 생명주기에 대한 깊은 이해를 얻을 수 있었습니다.