Reputation: 129
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
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