AJ-
AJ-

Reputation: 1783

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

Use case of the app:

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?

Upvotes: 1

Views: 639

Answers (2)

Akbar Asghari
Akbar Asghari

Reputation: 691

try this solution

my code was like this

providers: [
        ChangeNotifierProvider(create: (ctx) => Auth()),
        ChangeNotifierProxyProvider<Auth, Products>(
          create: (ctx) => Products(null, null, []),
          update: (ctx, auth, previousProducts) => Products(
              auth.token! ,
              auth.userId! ,
              previousProducts == null ? [] : previousProducts.items),
        ),
        ChangeNotifierProvider(create: (ctx) => Cart()),
        ChangeNotifierProxyProvider<Auth, Orders>(
          create: (ctx) => Orders(null, null, []),
          update: (ctx, auth, previousOrders) {
            print(auth);
            return Orders(auth.token!, auth.userId!,
                previousOrders == null ? [] : previousOrders.orders);
          },
        ),
      ],

and fix that by change ! to ?? like this code

ChangeNotifierProxyProvider<Auth, Products>(
          create: (ctx) => Products(null, null, []),
          update: (ctx, auth, previousProducts) => Products(
              auth.token ?? null,
              auth.userId ?? null,
              previousProducts == null ? [] : previousProducts.items),
        ),

Upvotes: 0

Patrick O&#39;Hara
Patrick O&#39;Hara

Reputation: 2229

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.

Upvotes: 1

Related Questions