`setState() or markNeedsBuild() called during build` 에러의 원인
정진규 Software Engineer 노션에 따르면 마치 Null Pointer Exception과 같은 광범위한 에러이다. 따라서 에러가 발생하는 시점에 관한 코드를 자세히 살펴보는 것이 좋다. 에러의 원인은 말 그대로 UI를 이미 빌드하고 있는데 해당 UI가 구독하고 있는 state에 변경이 일어나서 또 변경하게 생겼다는 것이다.
`setState() or markNeedsBuild() called during build` 에러 원인 분석
나의 경우에는 TestPage에 진입을 시도할 때마다 해당 에러가 발생하였다. 따라서 TestPage를 살펴보았다. TestPage는 TestState의 Consumer로서 TestState에 변경이 일어나면 rebuild를 하게 되어있다. 그런데 `build()` 부분에는 testState에 변경을 가하는 부분을 찾아볼 수 없었고, state를 읽어오는 부분 밖에 없었다.
진입을 시도할 때마다 해당 에러가 발생하는 점 때문에 TestPage `initState()`를 주목하였다. 그랬더니 아래 코드와 같이 UI를 빌드하는 코드에서 ChangeNotifier인 TestState class를 건드리고 있었다. (`testState.startNewTest()` 부분)
@override
void initState() {
super.initState();
final testState = context.read<TestState>();
audioTimer = testState.audioTimer;
testState.startNewTest();
// 퀴즈 종료 시 이동할 화면 설정
testState.setOnTestEnd(() {
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const EndingPage()),
);
}
});
}
즉, initState()가 실행된 다음에 build()가 실행되는데, initState()에 들어있는 testState.startNew()가 내부적으로 notifyListener()를 트리거하게 되고, build()가 UI를 그리고 있는 와중에 이 변경을 감지하면 이를 막기 위해 flutter가 `setState() or markNeedsBuild() called during build` 에러를 발생시키는 것이다.
`setState() or markNeedsBuild() called during build` 에러 해결
build()가 모두 완료된 후에 상태변경을 보장할 수 있도록 `addPostFrameCallback`를 사용한다.
@override
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
final testState = context.read<TestState>();
audioTimer = testState.audioTimer;
testState.startNewTest();
// 퀴즈 종료 시 이동할 화면 설정
testState.setOnTestEnd(() {
if (mounted) {
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => const EndingPage()),
);
}
});
});
}
'Flutter' 카테고리의 다른 글
Flutter 광고 로딩 실패 시 재시도 로직 - Exponential Backoff, 점진적 대기 시간 증가 적용하기 (0) | 2025.05.24 |
---|---|
Flutter 앱 서명하는 법 (1) | 2025.04.16 |
Consider canceling any active work during "dispose" or using the "mounted" getter to determine if the State is still active. 에러 원인과 해결 방법 (0) | 2025.03.20 |
Flutter UI와 State의 분리 - @initState와 Callback 함수 사용 (0) | 2025.03.16 |
Unbounded height에 대한 이해 (0) | 2025.02.02 |