Flutter

[Flutter] setState() 호출 시 mounted 체크 자동화

devyong 2025. 4. 10. 19:50

Flutter에서 비동기 작업이 끝난 후 setState()를 호출할 때, 종종 이런 에러를 마주친 적 있을 것이다.

 

setState() called after dispose()

 

이 에러는 해당 위젯이 이미 dispose되었는데도 setState()를 호출했을 때 발생한다.

특히 Future, Timer, 또는 Stream 이후 상태를 변경하려는 경우 이런 상황이 자주 발생한다.

 

(대부분 Flutter개발자는 getX, provider, bloc 등 상태관리 패키지로 인해 setState()를 호출하지 않을 것이다.)

 

 

 mounted 체크가 필요할까?

StatefulWidget의 생명주기에서 dispose()가 호출되면 그 이후엔 더 이상 UI를 업데이트할 수 없다.
그런데 비동기 코드에서는 UI가 사라진 뒤에도 작업이 계속되고 있을 수 있다.

void fetchData() async {
  final result = await someApiCall();
  setState(() { // <-- dispose된 상태에서 이 코드가 실행되면 에러 발생
    _data = result;
  });
}

이때 mounted를 체크해주면 안전하게 방지할 수 있다.

 

 

1. safeSetState 확장 함수 만들기

간단하게 State에 확장 함수를 추가해서 mounted를 자동 체크할 수 있다.

extension StateExtension on State {
  void safeSetState(VoidCallback fn) {
    if (mounted) setState(fn);
  }
}

이제 기존의 setState 대신 safeSetState를 사용하면 됩니다:

safeSetState(() {
_data = result;
});

 

 

2. BaseState 클래스 만들어 재사용

BaseState 클래스를 만들어 상속받는 방법도 있습니다.
abstract class BaseState<T extends StatefulWidget> extends State<T> {
  void safeSetState(VoidCallback fn) {
    if (mounted) setState(fn);
  }
}

사용 예

class MyPage extends StatefulWidget {
  @override
  _MyPageState createState() => _MyPageState();
}

class _MyPageState extends BaseState<MyPage> {
  void _loadData() async {
    final result = await someApiCall();
    safeSetState(() {
      _data = result;
    });
  }
}

 

 

 

위 방식으로 불필요한 크래시를 예방하고, 코드도 더 깔끔하게 유지할 수 있다.

setState()를 호출하는 경우가 있다면, 어렵지 않으니 적용해보는 것도 좋을 것 같다.