Provider의 ChangeNotifier와 Dispose와 비동기 함수

2024. 6. 20. 16:14Programming/Flutter

반응형

  Flutter를 사용해서 코드를 작성하는 도중, 다음과 같은 에러가 발생했다.

{Provider} was used after being disposed.

 

  notifyListener()를 호출하는 비동기 함수를 실행한 뒤, 함수 실행이 완료되기 전에 다른 페이지로 이동했기 때문에, dispose()가 호출된 다음 notifyListener()가 호출되어 발생한 에러 문구다. State에는 mounted라는 상태값이 있어서 dispose()가 된 이후에는 setState()를 호출하지 않을 수 있었으나, Provider는 기본적으로 dispose()가 실행됐는지 확인할 방법을 알려주지 않는다. 비동기 함수를 다루다보면 쉽게 만날 수 있는 문제여서 그런지, 에러 문구를 구글링해보니 쉽게 대응 방법을 찾을 수 있었다. 스택 오버플로우: Flutter changeNotifier was used after being disposed에 달려있는 답변을 살펴보니, ChangeNotifier에서 제공하는 notifyListener()disepose()를 override해서 쉽게 해결할 수 있었다. 문제를 해결하기 위해 ChangeNotifier를 상속받아서 작성한 코드는 다음과 같았다.

  class CustomChangeNotifier extends ChangeNotifier {
      bool _isMounted = false;
    bool get isMounted => _isMounted;

      @override
      void dispose() {
        _isMounted = false;
        super.dispose();
      }

      @override
      void notifyListeners() {
        if (_isMounted) {
          super.notifyListeners();
        }
      }
  }

 

  간단하다. 만들어놓고 곰곰히 생각해보니 비동기 요청을 하는 동안 프로그레스 인디케이터를 표시하고, 요청이 완료되면 프로그레스 인디케이터를 숨김 처리하는 페이지가 상당히 많았다. 기왕 ChangeNotifier를 상속받은 김에, 비동기 함수가 실행중인지 아닌지 여부를 체크하기 위한 상태값 _isFetching을 추가해보는 것은 어떨까. 또한 함수 내에서 비동기 처리를 실행하기 전에 _isFetchingtrue로 변경한 뒤 notifyListener()를 호출하고, 비동기 처리가 완료된 뒤에는 _isFetchingfalse로 변경한 뒤 notifyListener()를 호출하게 하면, 각 페이지마다 수동으로 코드를 작성할 필요가 없지 않을까.

 

  이를 위해서 다음과 같이 _isFetching값을 추가하고, 비동기 함수를 처리하기 위한 함수를 추가했다.

class CustomChangeNotifier extends ChangeNotifier {
      bool _isMounted = false;
    bool get isMounted => _isMounted;

    bool _isFetching = false;
    bool get isFetching => _isFetching;
    set isFetching(bool value) {
        _isFetching = value;
        notifyListener();
    }

    Future<T> asyncHandler(Future<T> Function() handler) async {
        isFetching = true;
        T result = await handler();
        isFetching = false;

        return result;
    }

      @override
      void dispose() {
        _isMounted = false;
        super.dispose();
      }

      @override
      void notifyListeners() {
        if (_isMounted) {
          super.notifyListeners();
        }
      }
  }

  새로 추가한 asyncHandler()는 인자로 전달받은 handler()를 실행하기 전에 isFetchingtrue로 변경해주고, handler()가 동작이 완료되면 isFetchingfalse로 변경해준다. 이걸로 Provider를 사용하는 페이지에서 비동기를 처리하기가 조금 더 쉬워졌다. 와! 고민 해결! ' ㅇ')

반응형