0

I want to retrieve color from firestore. But everytime I do I get this below error:

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

Here is my code:

 bool cardColor = false;
  String _userId;

  @override
  void initState() {
    super.initState();
    checkIfColorOrNot();
  }

  checkIfColorOrNot() async {
    FirebaseAuth.instance.currentUser().then((user) {
      _userId = user.uid;
    });
    DocumentSnapshot ds = await Firestore.instance
        .collection('rackBookItems')
        .document(widget.rackBookItems.id)
        .collection('user')
        .document(_userId)
        .get();
    this.setState(() {
      cardColor = ds.exists; // If the above if exists then cardColor  is turned true else it stays flase
    });
  }

  

_cardColorApply(child) {
    FirebaseAuth.instance.currentUser().then((user) {
      _userId = user.uid;
    });
    return StreamBuilder(
      stream: cardColor 
          ? Firestore.instance
              .collection('rackBookItems')
              .document(widget.rackBookItems.id)
              .collection('user')
              .document(_userId)
              .snapshots() // this should be shows only when cardColor is true
          : Firestore.instance
              .collection('rackBookItems')
              .document(widget.rackBookItems.id)
              .snapshots(), // this should be shows only when cardColor is false
      builder: (context, snapshot) {
        
       //Check to make sure snapshot.hasData has data or not 
        if (!snapshot.hasData) {
          return CircularProgressIndicator();
        }
        int colorValue = int.parse(snapshot.data['color']);
        return Card(
          color: Color(colorValue),
          child: child,
        );
      },
    );
  }



@override
      Widget build(BuildContext context) {
        return InkWell(
          onTap: widget.onTap,
          child: _cardColorApply(_listItems()),
        );
      }

I am using statefull widget for this. The the info under document(_userId) is added last as so this will be initially null and so when its null want to access document(widget.rackBookItems.id) for color info. Let me know if need any more information to get solution for this.

when cardColor = false my database will be like this so it can access color from document(widget.rackBookItems.id)

enter image description here

after doing some task the database changes to this below one so cardColor changed to true and also color can be accessed from document(_userId)

enter image description here ERROR:

enter image description here

Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
Jyo
  • 421
  • 1
  • 7
  • 20
  • its saying that snapshot.data is null. Can you show us how your database looks like? – CoderUni Aug 17 '20 at 09:38
  • yes sure, I have added screenshot of database that you asked for. – Jyo Aug 17 '20 at 09:55
  • I meant your database structure. Can you try printing out snapshot.data to check if its null or not? – CoderUni Aug 17 '20 at 09:58
  • when I give print(snapshot.data); it shows this : I/flutter (31146): Instance of 'DocumentSnapshot' – Jyo Aug 17 '20 at 10:03
  • try this `!snapshot.hasData && snapshot.data.isNotEmpty` – Sumeet.Jain Aug 17 '20 at 10:05
  • I did try this earlier and now again I tried, it gives me the same error : The method '[]' was called on null. Receiver: null Tried calling: []("color") – Jyo Aug 17 '20 at 10:08

1 Answers1

3

_userId is returning null, that's why you are not getting any data. You need to create the following methods:

  Stream<DocumentSnapshot> getUserDocument() async* {
    FirebaseUser user = await getCurrentUser();
    yield* Firestore.instance
        .collection('rackBookItems')
        .document(widget.rackBookItems.id)
        .collection('user')
        .document(user.uid)
        .snapshots();
  }


  Stream<DocumentSnapshot> getRackDocument() async* {
    yield* Firestore.instance
        .collection('rackBookItems')
        .document(widget.rackBookItems.id)
        .snapshots();
  }

  Future<FirebaseUser> getCurrentUser() async {
    return await FirebaseAuth.instance.currentUser();
  }

Then inside the checkIfColorOrNot() add the retrieval of the document inside the callback, to make sure that it gets execute after retrieving the userId:

  checkIfColorOrNot() async {
    FirebaseAuth.instance.currentUser().then((user) {
      _userId = user.uid;
      DocumentSnapshot ds = await Firestore.instance
          .collection('rackBookItems')
          .document(widget.rackBookItems.id)
          .collection('user')
          .document(_userId)
          .get();
      this.setState(() {
        cardColor = ds.exists; // If the above if exists then cardColor  is turned true else it stays flase
      });
    });
  }

In the StreamBuilder do the following:

_cardColorApply(child) {
    return StreamBuilder(
      stream: cardColor ? getUserDocument() : getRackDocument(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return CircularProgressIndicator();
        }
       else if(snapshot.hasData){
        int colorValue = int.parse(snapshot.data['color']);
        return Card(
          color: Color(colorValue),
          child: child,
        );
      }
Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
  • Thank you Peter for the your help. But there is small issue, I no longer get the error but the app does not do what I wanted it to. cardColor when false it should show the color that's in getRackDocument() and when cardColor is true it should show the color that's in getUserDocument(), currently cardColor even if its true it shows the color that's in getRackDocument(). Could you help me out on this part. – Jyo Aug 17 '20 at 10:38
  • inside the method `_cardColorApply(child) {` can you do `print(cardColor)` – Peter Haddad Aug 17 '20 at 10:49
  • cardColor is still showing false even when its generated in firestore. One more thing user.uid generated in this cod does not match the user ID generated in the firebase. Any idea how to fix this? – Jyo Aug 17 '20 at 13:37
  • In your code you are doing `document(_userId)` which means you are assigning the currently logged in user id to the document.. Therefore in the database you should have the user id as a document id... If you dont have that then you need to change your database – Peter Haddad Aug 17 '20 at 13:40
  • That's Correct. I have my user Id as my document ID which is stored in firestore. But when I print(_userId) this does not give out that current user Id. Still figuring out what went wrong. – Jyo Aug 17 '20 at 13:48
  • did you follow the code i gave you? `user.uid` should give u currently logged in user's id, if its not in the database then there is something wrong when inserting – Peter Haddad Aug 17 '20 at 13:51
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219973/discussion-between-jyo-and-peter-haddad). – Jyo Aug 17 '20 at 13:54
  • user.uid is fine it matched the document ID but cardColor is still showing as false. – Jyo Aug 17 '20 at 14:42
  • @Jyo Did you check what I answered in chat? – Peter Haddad Aug 18 '20 at 06:28
  • Yes looking into it. As of now things are still the same, cardColor is still showing as false. – Jyo Aug 18 '20 at 08:05
  • Have you tried the suggestion? can you try and debug it, add breakpoint to check if it is entering setState before the build method If it is not entering it before the build, then try this https://stackoverflow.com/questions/51901002/is-there-a-way-to-load-async-data-on-initstate-method/51901311#51901311 if the above also didnt work then you have to do another FutureBuilder @Jyo – Peter Haddad Aug 18 '20 at 08:07
  • After struggling for hours and with all your help @Peter Issue is fixed. I usedWidgetsBinding.instance.addPostFrameCallback((_) { // executes after build }). Which at first did not give any solution but now its working all fine. I appreciate all your help. Thank you again. – Jyo Aug 18 '20 at 08:27