NoSuchMethodError: The method '[]' was called on null. Receiver: null Tried calling: []("favorites")

1.1k Views Asked by At

On each user auth and user creation, I have an error message where the method is called on null.

The user is created when I sign up but it prevents me to continue the use without having to restart the apps.

I placed all the call related to user creation in firebase, I can not find the root cause of the null.

enter image description here


class AuthNotifier with ChangeNotifier {
  FirebaseUser _user;
  FirebaseUser get user => _user;

  void setUser(FirebaseUser user) {
    _user = user;
    _createUserDocument();
    if (user != null) {
      _createUserDocument();
    }
    notifyListeners();
  }

  void _createUserDocument() async {
    CollectionReference users = Firestore.instance.collection('Users');
    DocumentSnapshot user_document = await users.document(_user.uid).get();
    if (!user_document.exists) {
      // Create a document for the user if he doesn't have one
      users.document(_user.uid).setData({
        'favorites': []
      }
      );
    }
  }

  Future<void> toggleFavorite(String foodId) async {
    CollectionReference users = Firestore.instance.collection('Users');
    DocumentSnapshot user_document = await users.document(_user.uid).get();

    // If using collectionGroup, we can directly filter the foodId without looping through it.
    // Fetch and see if it is already in the list // TODO: Use class-wide, static, event-updated instance
    for (dynamic reference in user_document.data['favorites']) {
      if (reference.documentID == foodId) {
        // Remove it from the list and exit so it doesn't get added later in the function
        users.document(_user.uid).updateData({'favorites': FieldValue.arrayRemove([reference])}).catchError((error) => print("Failed to remove favorite element: ${error}"));
        notifyListeners();
        return;
      }
    }

    // Get the matching food DocumentReference object
    var food = await Firestore.instance.collection('Foods').document(foodId).get();
    // Add to the favorite list if it wasn't
    users.document(_user.uid).updateData({'favorites': FieldValue.arrayUnion([food.reference])}).catchError((error) => print("Failed to add new favorite element : ${error}"));
    notifyListeners();
  }

  Future<bool> isFavorite(String foodId) async {
    List<dynamic> favorites = await _getFavorites();
    for (DocumentReference favorite in favorites) {
      if (favorite.documentID == foodId) {
        return true;
      }
    }
    return false;
  }

  Future<List<dynamic>> getFavorites() async {
    List<dynamic> favorites = await _getFavorites();
    List<String> favorites_documentid = [];
    favorites.forEach((reference) { favorites_documentid.add(reference.documentID); });
    return favorites_documentid;
  }

  Future<List<dynamic>> _getFavorites() async {  // TODO: Use class-wide list that updates only when a a favorite is added or removed. Even listen to a snapshot for this, if there are lots of favorites.
    DocumentSnapshot snapshot = await Firestore.instance
        .collection('Users').document(_user.uid).get();
    return snapshot.data['favorites'];  // DocumentReference
  }

}

1

There are 1 best solutions below

4
On

You need to await for async operations to complete.


  Future<void> setUser(FirebaseUser user) {
    _user = user;
    await _createUserDocument();
    if (user != null) {
      await _createUserDocument();
    }
    notifyListeners();
  }

  Future<void> _createUserDocument() async {
    CollectionReference users = Firestore.instance.collection('Users');
    DocumentSnapshot user_document = await users.document(_user.uid).get();
    if (!user_document.exists) {
      // Create a document for the user if he doesn't have one
      await users.document(_user.uid).setData({
        'favorites': []
      }
      );
    }
  }

And await for setUser() as well.

Update. Use snapshot.data() instead of snaphot.data:

  Future<List<dynamic>> _getFavorites() async { 
    DocumentSnapshot snapshot = await Firestore.instance
        .collection('Users').document(_user.uid).get();
    return snapshot.data()['favorites'];  // DocumentReference
  }