ChangeNotifierProxyProvider flutter not getting update from ChangeNotifierProvider

Issue

I am trying to access userModel.uid from UserProvider inside the ItemProvider so it can load _savedItems.

However, when I call loadUserSavedItems(), _userId is null.

Here is my main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/user.dart';
import 'providers/item.dart';
import 'screens/home.dart';


void main() {
  Provider.debugCheckInvalidValueType = null;
  WidgetsFlutterBinding.ensureInitialized();

  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<UserProvider>(
            create: (_) => UserProvider.initialize()),
        ChangeNotifierProxyProvider<UserProvider, ItemProvider>(
          create: (_) => ItemProvider(),
          update: (_, userProvider, itemProvider) => itemProvider,
        ),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primaryColor: Colors.white,
          primaryTextTheme: TextTheme(
            headline6: TextStyle(color: Colors.black),
          ),
        ),
        home: Home(),
      ),
    ),
  );
}

UserProvider.dart


import 'models/user.dart';
import 'services/user.dart';

enum Status { Uninitialized, Authenticated, Authenticating, Unauthenticated }

class UserProvider extends ChangeNotifier {
  FirebaseAuth _auth;
  FirebaseUser _user;
  Status _status = Status.Uninitialized;
  Firestore _firestore = Firestore.instance;
  UserServices _userServicse = UserServices();
  UserModel _userModel;

//  getter
  UserModel get userModel => _userModel;
  Status get status => _status;
  FirebaseUser get user => _user;

  UserProvider.initialize() : _auth = FirebaseAuth.instance {
    _auth.onAuthStateChanged.listen(_onStateChanged);
    notifyListeners();
  }

  Future<void> _onStateChanged(FirebaseUser firebaseUser) async {
    if (firebaseUser == null) {
      _status = Status.Unauthenticated;
    } else {
      _user = firebaseUser;
      _status = Status.Authenticated;
      _userModel = await _userServicse.getUserById(user.uid);
    }
    notifyListeners();
  }

  Future<bool> signIn() async {
    bool retVal = false;
    try {
      _status = Status.Authenticating;
      notifyListeners();
      AuthResult _authResult = await _auth.signInWithEmailAndPassword(
          email: email.text.trim(), password: password.text.trim());
      if (_authResult.user != null) {
        retVal = true;
      }
    } catch (e) {
      _status = Status.Unauthenticated;
      notifyListeners();
      print(e.toString());
    }
    return retVal;
  }
}

ItemProvider.dart

import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/foundation.dart';
import 'models/item.dart';
import 'providers/user.dart';
import 'services/item.dart';

class ItemProvider extends ChangeNotifier {
  ItemProvider({UserProvider userProvider}) : _userProvider = userProvider;

  UserProvider _userProvider;

  final databaseReference = Firestore.instance;
  ItemServices _itemServices = ItemServices();
  List<Item> _items = [];
  List<Item> _savedItems = [];
  String _userId;

  //  getter
  List<Item> get items => _items;
  List<Item> get savedItems => _savedItems;
  String get userId => _userId;
  UserProvider get userProvider => _userProvider;

  //get the items to load when the app is initiated
  ItemProvider.initialize() {
    loadAllActiveItems();
    if (_userProvider.status == Status.Authenticated) {
      loadUserSavedItems();
    }
  }

  //get active items from DB
  loadAllActiveItems() async {
    _items = await _itemServices.getBatchOfActiveItems();
    notifyListeners();
  }

  //get user saved items from DB
  loadUserSavedItems() async {
    _userId = userProvider.userModel.uid;
    _savedItems = await _itemServices.getUserSavedItems(userId);
    notifyListeners();
  }
}

Can someone help me, I am not quite sure what I am missing, can’t userModel object/properties in ItemProvider.dart when implemented as ChangeNotifierProxyProvider.

Solution

I finally found a way around, maybe not the best but it worked.

Inside ItemProvider created updates() to receive the userId from

  void updates(String userId, Status status) {
    this._userId = userId;
  }

And then made the changes in main.dart to pass the userId from ChangeNotifierProxyProvider.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'providers/user.dart';
import 'providers/item.dart';
import 'screens/home.dart';


void main() {
  Provider.debugCheckInvalidValueType = null;
  WidgetsFlutterBinding.ensureInitialized();

  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider<UserProvider>(
            create: (_) => UserProvider.initialize()),
        ChangeNotifierProxyProvider<UserProvider, ItemProvider>(
          create: (_) => ItemProvider(),
          update: (_, userProvider, itemProvider) => itemProvider
            ..updates(
                userProvider.user != null ? userProvider.user.uid : ''),
        ),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(
          primaryColor: Colors.white,
          primaryTextTheme: TextTheme(
            headline6: TextStyle(color: Colors.black),
          ),
        ),
        home: Home(),
      ),
    ),
  );
}


Happy to hear what others have done as well.

Answered By – Vibor

Answer Checked By – Senaida (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *