NinFudj
NinFudj

Reputation: 129

Setting and retrieving user profile flutter

New to dart/flutter. I've been following some tutorials on how to properly set up a user profile and be able to make it available in memory instead of having to fetch details from firebase and to get it in one place. But I have hit some late initialization errors when trying to retrieve the data.

here's my user model:


    class LakoUser  {
    
      //todo fill this with the necessary fields to create just 1 place to

      // retrieve the user's data that is global.... bad idea?
      // I tried extending this with User Class but got confused. I was wishing I can get some 
      // other fields of the User class for later use.

      final  String  uid;
  
      final String? username;
      final String? buyerMarker;
      final String? email;
      final String? mobileNum;
    
      LakoUser(
          {
            required this.uid,
            this.username,
            this.buyerMarker,
            this.email,
            this.mobileNum,
          });
    
      
       LakoUser.fromData(Map<String, dynamic> data)
        : uid = data['buyerId'],
          username = data['username'],
          buyerMarker = data['buyerMarker'],
          email = data['email'],
          mobileNum = data['mobileNum'];

    // storing to json format for quick saving to db
    Map<String, dynamic> toJson() {
      return {
        'buyerId': uid,
        'username': username,
        'buyerMarker': buyerMarker,
        'email': email,
        'mobileNum': mobileNum,

      };}

    factory LakoUser.fromJson(Map <String,dynamic> json)  {
       return LakoUser(
                 uid: json['buyerId'],
                 username: json['username'],
                 buyerMarker : json['buyerMarker'],
                 email: json['email'],
                 mobileNum: json['mobileNum']
       );}}

AuthService:

class AuthService {

  final FirebaseAuth _auth = FirebaseAuth.instance;
  DatabaseService db = DatabaseService();

  late LakoUser _currentUser;
  LakoUser get currentUser  => _currentUser;

  

  //returns a AuthResult object that can ba a User or null once created/called thru registrations or signins
  LakoUser? _userfromSignIn(User? user) {

    return user != null ? LakoUser(uid: user.uid) : null;
  }

  //Fill _currentUser with data from the logged in user taken from db earlier
   populateCurrentUser(String? user) async {

    if (user != null) {
      //currentUser is a map object
      _currentUser = await db.getUser(user);
      print(_currentUser.mobileNum);
    }
  }

 
  Stream<LakoUser?> get user {

    // A sign-in or out is monitored and sent to 'user' parameter of _userfromSignIn
    return _auth.authStateChanges()
        .map((User? user) => _userfromSignIn(user));

  }
Future signInWithEmailAndPassword(String email, String password) async {

try {
  UserCredential resultOfSignIn = await _auth.signInWithEmailAndPassword(email: email, password: password);
  User? user = resultOfSignIn.user;

  await populateCurrentUser(user?.uid); //populate  this works. can confirm it fetches data
  return _userfromSignIn(user);

}catch(e) {
  print(e.toString());
  // return e;
  return null;
}

}

My Database Service:

Future  getUser(String uid) async {

//todo: fetch data from buyersCollection and save it a json
try {
  var document = await buyersCollection.doc(uid).get();
   Map <String, dynamic> userData = document.data() as Map <String, dynamic>;
  

  // var userData = await buyersCollection.doc(uid).get();

  return LakoUser.fromJson(userData);

} catch (e) {
  return e;
}

}

Im trying to setup a profile setting page:

  class ProfileSettings extends StatefulWidget {
  const ProfileSettings({Key? key}) : super(key: key);

  @override
  _ProfileSettingsState createState() => _ProfileSettingsState();
}

class _ProfileSettingsState extends State<ProfileSettings> {

  String? username = "";
  String? email = "";
  final usernameCont = TextEditingController(text: "");
  final emailCont = TextEditingController(text: "");
  DatabaseService db = DatabaseService();


  final _profileSettingsKey = GlobalKey <FormState>();

  bool disableFocus = true;
  String? avatar = "";

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

   ....  the Widget tree ...

Future <String?> prefillFields() async {
    //tried calling global auth and making an instance of AuthService here same error.    
    await auth.populateCurrentUser(auth.currentUser.uid);   <--- error begins here

    //how to retrieve the values so ican place them on the widgets below?
    // ignore the code below as i was trying to refactor it to get data from LakoUser instead of db

    username = db.username;

    usernameCont.value = (TextEditingValue(text: username.toString()));

    email = db.email;
    emailCont.value = (TextEditingValue(text: email.toString()));

    setState(() {

    });
    return null;
  }

It seems that even right after signin I lose the data in currentUser:

    Unhandled Exception: LateInitializationError: Field '_currentUser@163456873' has not been initialized. E/flutter (31679): #0 AuthService._currentUser ...
E/flutter ( 6069): #0      AuthService.currentUser (package:mlako/services/auth.dart)

How to properly store it after registration/signin or just after startup when the app already remembers the last logged in user? Have so many questions. Thanks in advance!

Upvotes: 0

Views: 1878

Answers (1)

Denzel
Denzel

Reputation: 1109

So you're not losing the current user, I assume you are using FilledStacks tutorial because he is the one that writes a populateUser function explicitly.

Your error is very clear and what's happening is that you have not initialized your auth, because you have to initialize an instance of firebase before you can get the uid. So something like this,

  class ProfileSettings extends StatefulWidget {
  const ProfileSettings({Key? key}) : super(key: key);

  @override
  _ProfileSettingsState createState() => _ProfileSettingsState();
}

class _ProfileSettingsState extends State<ProfileSettings> {

  String? username = "";
  String? email = "";
  final usernameCont = TextEditingController(text: "");
  final emailCont = TextEditingController(text: "");
  DatabaseService db = DatabaseService();


  final _profileSettingsKey = GlobalKey <FormState>();

  bool disableFocus = true;
  String? avatar = "";

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

   ....  the Widget tree ...

Future <String?> prefillFields() async {
final authInstance = FirebaseAuth.instance; 

    await auth.populateCurrentUser(authInstance.currentUser.uid);   


    username = db.username;

    usernameCont.value = (TextEditingValue(text: username.toString()));

    email = db.email;
    emailCont.value = (TextEditingValue(text: email.toString()));

    setState(() {

    });
    return null;
  }

Also make sure you are calling the service locator function in your main function.

Upvotes: 1

Related Questions