Getting and storing user data after logging/sign up

Viewed 3392

I want to load user profile information from firestore after the user logs in and I want that data to be available to the whole app when the data is logged in. I plan on doing the later part using inherited widgets in flutter. I can load the data from firestore correctly and get all the data but the home page does not wait for the data to load before continuing so I get errors. It calls the method, then ignores the await part in the method and proceeds on, so I get errors, then after the errors, I get the data messages that the data has loaded.

I have tried a couple of things. I have all my user management code (login, sign up, ...) in one file and I wrote a getuserProfie() method in that file. The method gets all the data, populates a user model with the data and returns that model. See code below.

 getUserProfile() async {
    UserModel theUser;
    await FirebaseAuth.instance.currentUser().then((user) {
     Firestore.instance
      .collection('/users')
      .where('uid', isEqualTo: user.uid)
      .snapshots()
      .listen((data) {
    print('getting data');
    theUser = new UserModel(user.uid, data.documents[0]['email'],);

    print('got data');
    print(theUser.email);
    return theUser;
  });
}).catchError((e) {
  print("there was an error");
  print(e);
});
}

I then call this method in my home page as follows

      @override
      void initState() {
      Usermode user = UserManagement().getUserProfile(); //Calling the method
      super.initState();
      }

This calls the method just fine and gets all the data but the program does not wait until the data is gotten to proceed. I tried moving the code that gets the data into the initState() method and calling super.initState() when I was sure there was a value but that did not work. I also tried calling the getUserProfile() before calling super.initstate() but that did not work.

I tried adding async to the initState() header but flutter does not like that. I can get the data from firebase fine but making the program wait until the data is gotten is the problem I am having. Seems like using await and async is not working. Any other suggestions to make sure the data is loaded before continuing ? I thought about using FutureBuilder but I am not sure how that would work in this case.

3 Answers

initState() can't be a async function.

What You Can try is - Move Usermode user = await UserManagement().getUserProfile(); to a new async function.

Usermode user;

@override
void initState() {
super.initState();
_getUser();

}

Void _getUser() async {
user = await UserManagement().getUserProfile();
setState(() {});
}

and in Build Method you can Check for User Value then pass the Widgets.

@override
  Widget build(BuildContext context) {
    if (user != null) {
      return ProfilePage();
    } else {
      return CircularProgressIndicator();
    }

Now whenever User Data is Loaded it will call setState() & your Profile Page will Load.

For this you need to make - Usermode user; state variable.

Since getUserProfile is async, you'll need to use await when calling it:

@override
void initState() async {
  Usermode user = await UserManagement().getUserProfile();
  super.initState();
}

After doing much research, I ended up using Flutter StreamBuilder. It was perfect for what I wanted to use it for. I call the method UserManagement().getUserProfile() and get the snapshot of the data from the returned value. If the data is loading, I display the CircularProgressIndicator(). If the snapshot is null, I display error message. See code snippet below.

    @override
   Widget build(BuildContext context) {
   return StreamBuilder(
    stream: UserManagement().getClientProfile(curUserID).snapshots(),
    builder: (context, snapshot) {
      if (snapshot.connectionState == ConnectionState.active) {
      UserModel _user = new UserModel.from(snapshot.data)
        return Scaffold(
            body: Column(
                crossAxisAlignment: CrossAxisAlignment.center,
                children: <Widget>[
                 Text(_usermodel.name),
            ],
       });
     }else if (snapshot.connectionState == ConnectionState.waiting) {
        return Container(child: Center(child: CircularProgressIndicator()));
      } else {
        return Container(
          child: Column(
            children: <Widget>[
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Icon(Icons.warning),
              ),
              Text('There was an error please try again')
            ],
          ),
        );
      }
  }

In this example, I am using StreamBuilder but you can also use FutureBuilder. StreamBuilder listens for changes in the data and updates the UI accordingly while FutureBuilder gets the data once until the page is loaded again. I had to make changes to my UserModel and also adjusted the way I stored my data but it all worked out. Got most of my solution from here

Related