ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Riverpod] Unhandled Exception: Tried to modify a provider while the widget tree was building.
    Programing/Flutter 2024. 6. 9. 21:52
    반응형

     

    어떤 화면에 로딩 인디케이터(loading indicator - 작업 완료될 때 까지 뱅글뱅글)를 넣어놨는데,

    작업이 완료되기 전에 해당 화면에서 빠져나갈 경우, 다시 그 화면으로 들어갔을 때

    무한정 뱅글뱅글 돌아가는 에러가 있음을 확인했습니다.

     

    그래서 그 화면에 들어가자마자 indicator.status = false로 설정해두면 되겠지... 하면서 다음과 같이 코드를 작성했습니다.

    import 'package:flutter/material.dart';
    import 'package:flutter_riverpod/flutter_riverpod.dart';
    import 'package:projects/common/w_loading_indicator.dart';
    
    class ChatScreen extends ConsumerStatefulWidget {
      const ChatScreen({super.key});
    
      @override
      ChatScreenState createState() => ChatScreenState();
    }
    
    class ChatScreenState extends ConsumerState<ChatScreen> {
    
      @override
      void initState() {
        super.initState();
        ref.read(loadingProvider.notifier).state = false;
      }
    
    
    // 기타 코드 생략

     

    그런데 사진처럼 무섭게 생긴 에러가 발생했습니다.

    E/flutter ( 5343): [ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Tried to modify a provider while the widget tree was building.
    E/flutter ( 5343): If you are encountering this error, chances are you tried to modify a provider
    E/flutter ( 5343): in a widget life-cycle, such as but not limited to:
    E/flutter ( 5343): - build
    E/flutter ( 5343): - initState
    E/flutter ( 5343): - dispose
    E/flutter ( 5343): - didUpdateWidget
    E/flutter ( 5343): - didChangeDependencies
    E/flutter ( 5343): 
    E/flutter ( 5343): Modifying a provider inside those life-cycles is not allowed, as it could
    E/flutter ( 5343): lead to an inconsistent UI state. For example, two widgets could listen to the
    E/flutter ( 5343): same provider, but incorrectly receive different states.
    E/flutter ( 5343): 
    E/flutter ( 5343): 
    E/flutter ( 5343): To fix this problem, you have one of two solutions:
    E/flutter ( 5343): - (preferred) Move the logic for modifying your provider outside of a widget
    E/flutter ( 5343):   life-cycle. For example, maybe you could update your provider inside a button's
    E/flutter ( 5343):   onPressed instead.
    E/flutter ( 5343): 
    E/flutter ( 5343): - Delay your modification, such as by encapsulating the modification
    E/flutter ( 5343):   in a `Future(() {...})`.
    E/flutter ( 5343):   This will perform your update after the widget tree is done building.
    E/flutter ( 5343): #0      _UncontrolledProviderScopeElement._debugCanModifyProviders (package:flutter_riverpod/src/framework.dart:349:7)
    E/flutter ( 5343): #1      ProviderElementBase._notifyListeners.<anonymous closure> (package:riverpod/src/framework/element.dart:471:34)
    E/flutter ( 5343): #2      ProviderElementBase._notifyListeners (package:riverpod/src/framework/element.dart:473:8)
    
    // 이후 생략

     

    아직 위젯트리가 다 생성되지 않았는데 provider(Riverpod)가 작동해서 충돌이 일어났기 때문에 발생한 문제인 것 같습니다.

     

     

    해결

    화면 렌더링을 마친 이후에 status를 변경해주도록 합시다.

    class ChatScreen extends ConsumerStatefulWidget {
      const ChatScreen({super.key});
    
      @override
      ChatScreenState createState() => ChatScreenState();
    }
    
    class ChatScreenState extends ConsumerState<ChatScreen> {
    
      @override
      void initState() {
        super.initState();
        WidgetsBinding.instance.addPostFrameCallback((_) {
          // 화면 렌더링 완료 후 작업 진행
          ref.read(loadingProvider.notifier).state = false;
        });
      }
    
    // 기타 코드 생략

     

     

    상세 설명

    왜 addPostFrameCallback이 필요한가?

    1. 상태 불일치 방지:
      • 초기화 과정 중에 Flutter가 여러 번 프레임을 갱신할 수도 있습니다.
        만약 프레임 중간에 상태를 변경하면 불필요한 재렌더링이나 상태 불일치가 발생할 수 있습니다.
        때문에 initState에서 직접 Provider의 status를 설정하지 않고 프레임 렌더링이 끝난 후에 설정합니다.
    2. 초기화 로직과 렌더링 분리:
      • 특정 작업이 렌더링 후에 수행되어야 하는 경우, addPostFrameCallback을 사용하여 이를 보장합니다.
        이는 초기화 로직과 렌더링 로직을 명확히 분리할 수 있게 해줍니다.
        (위의 제 사례에서는 그런 경우는 아닙니다.)
    3. 안전한 상태 업데이트:
      • 위젯이 처음 빌드될 때 상태를 안전하게 업데이트할 수 있게 합니다.
        빌드 과정이 완료된 후에 상태를 업데이트함으로써 무한 루프나 성능 문제를 피할 수 있습니다.

     

     

    참고한 자료: https://flutter.salon/error_warning/modify_provider_while_widget_building/

     


    반응형

    댓글

Designed by Tistory.