Flutter 개발 상자

[Flutter] [Riverpod] Tried to read the state of an uninitialized provider 본문

Flutter/오류해결

[Flutter] [Riverpod] Tried to read the state of an uninitialized provider

망고상자 2023. 12. 26. 16:26
728x90

오류의 의미

초기화 되지 않은 Provider의 상태를 읽으려고 해서 문제가 발생했다.

 

원인찾기

일단 초기화 되지 않은 Provider를 쓰고 있는지 확인해본다.

 

하지만 Riverpod은 Provider를 등록하고 사용한다는 개념이 없어서 override로 초기화 해서 사용하기 위해 일부러 throw 를 던지는게 아니라면 딱히 초기화 문제는 발생하지 않는게 보통이다.

 

제네레이터를 사용한다면 이걸 의심해보자

Riverpod Generator 에서 클래스형태의 NotifierProvider를 구현할 경우 build 함수를 통해서 초기화를 진행하게 되어있다.

그런데 여기서 build 함수가 끝나기도 전에 state에 접근한다면 문제가 생길 수 있다.

예를 들면 아래와 같은 코드는 문제를 발생시킨다.

 

import 'package:riverpod_annotation/riverpod_annotation.dart';

part 'app_count.g.dart';

@riverpod
class AppCount extends _$AppCount {
  @override
  int build() {
    initData();
    return 0;
  }

  void initData() {
    if (state == 0) { // 이부분에서 문제 발생
      state = 10; // state에 대입 하는건 문제가 없다.
    }
  }
}

 

build() 함수가 끝나기 전에 if문에서 state값을 읽어왔으므로 오류가 발생한다.

이외에도 state를 freezed로 불변객체로 만든뒤 copywith 같은 동작을 해도 값을 읽는 동작이 들어가기 때문에 에러가 발생한다.

 

 

해결법은

1. build()에서 return전에 state를 읽지 않거나 꼭 읽어야 할 경우 state에 값이 있는지 분기처리를 해준다.

2. 그래도 state를 읽거나 copywith를 해야할 경우 Future를 통해서 함수의 실행을 지연시켜준다. 이렇게 구현하면 AsyncNotifier를 쓰지 않으면서 1회에 한하여 비동기값을 받아오는 Notifier를 구현하는것이 가능하다. 아래와 같은 느낌으로 구현해준다.

 

@freezed
class AppInfoState with _$AppInfoState {
  const factory AppInfoState({
    String? appVersion,
    String? appName,
  }) = _AppInfoState;
}

@riverpod
class AppInfo extends _$AppInfo {
  @override
  AppInfoState build() {
    initCurrentVersion();
    return const AppInfoState();
  }

  void initCurrentVersion() async {
    // await를 걸었기 때문에 await 밑의 함수들은 지연되서 실행된다.
    final PackageInfo packageInfo = await PackageInfo.fromPlatform();
    // 지연되는 동안 build() 함수쪽에서는 return이 먼저 실행되고 아래의 코드들이 실행된다.
    state = state.copyWith(
      appVersion: packageInfo.version,
      appName: packageInfo.appName,
    );
  }
}

 

위의 코드는 PackageInfo 패키지를 통해서 앱의 정보를 저장하는 프로바이더이다.

문제는 PackageInfo 패키지를 사용하기 때문에 비동기로 값을 받아와야 한다.

하지만 자주 변하는 값이 아니라 최초 1회에 한에서 받아오기만 하면 되는 값들이기에 굳이 asyncNotifier를 사용하지 않고 위와 같이 구현하였다.

 

UI 구현부에서는 null일때와 null이 아닐때를 분기처리하여 UI를 구성시켜주면 되겠다.

 

 

요약

  • NotifierProvider 사용시 build() 가 끝나기 전에 state를 읽어서는 안된다.
728x90