I have never been stuck for that long ever before. So folks, I really need your help.
I am using Flutter with Firebase. When user is registered, 2 entries are created in the DB. First is the default entry by Firebase, which stores UID and e-mail. Second is the entry in Firebase document user_data, which contains more information about the user ( level, avatar and etc ). There are 2 streams defined in 2 service classes. auth.dart and database.dart.
Authentication ( auth.dart ) stream looks as follows:
final FirebaseAuth _auth = FirebaseAuth.instance;
Stream<AppUser?> get user {
return _auth.authStateChanges().map(_userFromFirebaseUser);
}
AppUser? _userFromFirebaseUser(User? user) {
return user != null ? AppUser(uid: user.uid) : null;
}
Database (database.dart) stream looks as follows:
Stream<UserData>? get userData {
return userDataCollection.doc(uid).snapshots().map(_userDataFromSnapshot);
}
UserData _userDataFromSnapshot(DocumentSnapshot snapshot) {
return UserData(
uid: uid,
username: snapshot['username'],
email: snapshot['email'],
avatar: snapshot['avatar'],
level: snapshot['level'],
);
}
StreamProvider is defined in main class:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// ~ If the user is signed in, we want to show Home screen
// ~ so we need a way to provide that stream data to the root widget
// ~ so that it can listen to authChanges and provide the information down below
// ! we will use package " Provider " for this
return MultiProvider(
providers: [
StreamProvider<AppUser?>.value(
value: AuthService().user, initialData: null),
StreamProvider<UserData?>.value(
value: DatabaseService(uid: 'V5p7vIYlFNapXhQWFGE1m7MlwLq1').userData,
initialData: null)
],
child: MaterialApp(
home: Wrapper(),
routes: {
'/home': (context) => Home(),
'/userProfile': (context) => userProfile(),
},
),
);
}
}
And here is example of usage:
class NavBar extends StatefulWidget {
@override
State<NavBar> createState() => _NavBarState();
}
class _NavBarState extends State<NavBar> {
final AuthService _auth = AuthService();
String userName = 'test';
String userEmail = 'test';
String avatar = 'default.png';
@override
Widget build(BuildContext context) {
// ! Here we access userData from the prodiver
final userData = Provider.of<UserData?>(context);
if(userData != null){
userName = userData.username;
userEmail = userData.email;
return Drawer(
................
);
}else{
return Loading();
}
}
As you have noticed, I am hardcoding the userID in order for data to be fetched. But what I have is that as soon as user has logged in, then the StreamProvider<UserData?> is updated in the main class with UID.
I have spent 1 week watching videos about all providers, futures, streams, dependency injection and all this crap associated with this topic, and tried out different solutions (proxyProvider, changeNotifier, using Future to inform about change and overs). But nothing seems to work. I believe, that it is possible to update UID in StreamProvider and inform children about this. And I have a feeling, that this has to be done with ProxyProvider, but I am doing something wrong.
If you, my possible saviour, can help me out, I will name my kid after you.
Here is github link to the project. I tried to make the code as clear as possible. https://github.com/MinisX/quizApp_backend. And also the UID I have hardcoded is associated with this user. Username: chucky@gmail.com Password: 262782
P.S There is similar question and I have even contacted the guy directly who posted it, and he said he could not solve it. In the answer another persons metnioned ProxyProvider. Flutter: How to handle using StreamProvider, FireStore and using Drawer