Search code examples
flutterdartflutter-provider

Flutter - ProxyProvider how to fix error "Null check operator used on a null value"


Use case of the app:

  • at App start, it will fetch a random User from a RestAPI,
  • then it will use the result of the first call to make another RestAPI call, this is why I need ProxyProvider.

main.dart:

return MultiProvider(
    providers: [
      ChangeNotifierProvider(
        create: (_) => RandomUser(),
      ),
      ChangeNotifierProxyProvider<RandomUser, FavoriteList>(
        create: (BuildContext ctx) => FavoriteList(ctx.read<RandomUser>()),
        update: (_, RandomUser user, __) => FavoriteList(user),
      ),

RandomUser provider:

class RandomUser extends ChangeNotifier {
  final apiUsers = UsersApi();
  UserModel? _profile;
  String? _userId;

  RandomUser() {
    fetchUser();
  }

  Future<void> fetchUser() async {
    await apiUsers
      .apiGetUser()
      .then((user) => {
        _profile = user,
        _userId = chosenUserId,
      })
      .catchError((e) {
        print("error: $e");
    });

    notifyListeners();
  }

  UserModel get profile => _profile;

  String get chosenUserId => _userId;
}

FavoriteList provider:

class FavoriteList extends ChangeNotifier {
  final RandomUser _user;
  final _apiFavoriteList = FavoriteListApi();
  List<FavoriteListModel> _favoriteList = <FavoriteListModel>[];

  FavoriteList(this._user) {
    fetchFavoriteList(_user.chosenUserId);
  }

  Future<void> fetchFavoriteList(String userId) async {
   await _apiFavoriteList
      .apiGetFavoriteList(userId)
      .then((favoriteList) => {
        _favoriteList = favoriteList,
      })
      .catchError((e) {
        print("error: $e");
    });

    notifyListeners();
  }
  
  List<FavoriteListModel> get favoriteList => this._favoriteList;
}

So as you can see, the FavoriteList provider needs the RandomUser provider, to retrieve the getter value chosenUserId

When I launch the App, I get right away the error "Null check operator used on a null value" on the getter chosenUserId and in the main.dart where I call "create" of the ProxyProvider

What am I doing wrong? Shouldn't the ProxyProvider first initialized the first Provider, so all the values I need are available?


Solution

  • The issue is that RandomUser.fetchUser() has not completed before FavoriteList is created. You should code allowing for this situation, e.g. in RandomUser:

      String? get chosenUserId => _userId;
    

    and in FavoriteList:

      final? RandomUser _user;
    
      FavoriteList(this._user) {
        if (_user != null && _user?.chosenUserId != null) {
          fetchFavoriteList(_user.chosenUserId);
        }
      }
    
      String? get chosenUserId => _user?.chosenUserId;
    

    When fetchUser() completes then FavoriteList will be updated.

    Your UI will have to cope with the (temporary) missing data, of course.

    BTW the documentation for ChangeNotifierProxyProvider suggests that you should structure your code like this:

    ChangeNotifierProxyProvider<MyModel, MyChangeNotifier>(
      create: (_) => MyChangeNotifier(),
      update: (_, myModel, myNotifier) => myNotifier
        ..update(myModel),
      child: ...
    );
    

    In that situation, if MyModel were to update, then MyChangeNotifier will be able to update accordingly. Notice how MyChangeNotifier doesn't receive MyModel in its constructor anymore. It is now passed through a custom setter/method instead.