Flutter StreamProvider returning null despite stream returning objects

2,024

I think you have used the StreamProvider wrong way. In the following code you are creating a new UserData object.

   ListTile(
          title: Text(UserData().email),
        )

Here is aa example how you can use the StreamProvider by creating a separate Widget.

class Profile extends StatefulWidget {
  @override
  _ProfileState createState() => _ProfileState();
}

class _ProfileState extends State<Profile> {
  @override
  Widget build(BuildContext context) {

    return StreamProvider<UserData>.value(
      initialData: UserData.initialData(),
      value: DatabaseService(uid: user.uid).userData,
      child: SecondWidget(),
    );
  }
}

class SecondWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final user = Provider.of<UserData>(context);
    return Scaffold(
      appBar: AppBar(),
      drawer: NavigationDrawer(),
      body: ListView(
        children: <Widget>[
          ListTile(
            title: Text(user.email),
          )
        ],
      ),
    );
  }
}
Share:
2,024
Ben Naylor
Author by

Ben Naylor

Updated on December 19, 2022

Comments

  • Ben Naylor
    Ben Naylor over 1 year

    I'm try to set up a profile type page where users can view their information and eventually I'll add features so they can change their data. I'm using Provider to access a Firebase stream <User> which returns the current user's UID (this is working fine). The UID is then used to access their <UserData> stream, but when I try and print UserData().email or any other bits of data it just returns null. The code is as follows:

    class Profile extends StatefulWidget {
      @override
      _ProfileState createState() => _ProfileState();
    }
    
    class _ProfileState extends State<Profile> {
      @override
      Widget build(BuildContext context) {
    
        final user = Provider.of<User>(context);
    
        return StreamProvider<UserData>.value(
          initialData: UserData.initialData(),
          value: DatabaseService(uid: user.uid).userData,
          child: Scaffold(
            appBar: AppBar(),
            drawer: NavigationDrawer(),
            body: ListView(
              children: <Widget>[
                ListTile(
                  title: Text(UserData().email),
                )
              ],
            ),
          ),
        );
      }
    }
    

    The stream itself looks like this:

      UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
        return UserData(
          uid: uid,
          email: snapshot.data['email'],
          firstName: snapshot.data['firstName'],
          lastName: snapshot.data['lastName'],
          phone: snapshot.data['phone'],
          dateCreated: snapshot.data['dataCreated'],
          isVerified: snapshot.data['isVerified']
        );
      }
    
      //User data stream
      Stream<UserData> get userData {
        return userDataCollection.document(uid).snapshots()
            .map(_userDataFromSnapshot);
      }
    

    And the UserData class is just this

    class UserData {
    
      final String uid;
      final String email;
      final String firstName;
      final String lastName;
      final String phone;
      final DateTime dateCreated;
      final bool isVerified;
    
      UserData({ this.uid, this.email, this.firstName, this.lastName, this.phone, this.dateCreated, this.isVerified });
    
      factory UserData.initialData() {
        return UserData(
          uid: '',
          email: '',
          firstName: '',
          lastName: '',
          phone: '',
          dateCreated: null,
          isVerified: null,
        );
      }
    }
    

    And finally this is the error I'm getting from the ListTile because UserData().email is null, not a string:

    A non-null String must be provided to a Text widget.
    'package:flutter/src/widgets/text.dart':
    Failed assertion: line 285 pos 10: 'data != null'
    

    I thought it may have been an issue with the data not loading in time so when Flutter tries to build the page the data isn't there, but adding the UserData.initialData didn't seem to help. I have managed to get it working using StreamBuilder instead however I don't whether using that is best practice or if I should just do everything with Provider so any help wold be appreciated.

    Thanks

  • Ben Naylor
    Ben Naylor about 4 years
    Thanks very much, that works great, though I just kept the final user = Provider.of<User>(context); bit in _ProfileState as that provides the uid needed in the DatabaseService(uid: user.uid)