Null is not a subtype of type String - flutter

Hello I’m new to flutter
I’m trying to retrieve the user data from his email but i got this error [Null is not a subtype of type String]
The data I’m trying to retrieve is not null
This is my code
class _ProfilePageState extends State<ProfilePage> {
late User user;
final _auth = FirebaseAuth.instance;
late User signedInUser;
var id;
var email;
var name;
var age;
var sex;
#override
void initState() {
super.initState();
onRefresh(FirebaseAuth.instance.currentUser);
getCurrentUser();
}
onRefresh(userCare)
{
setState(()
{
user = userCare;
});
}
void getCurrentUser()
{
try {
final user = _auth.currentUser;
if (user != null) {
signedInUser = user;
email = signedInUser.email;
id = signedInUser.uid;
}
} catch (e) {
print(e);
}
}
void getData() {
FirebaseFirestore.instance
.collection('users')
.get()
.then((QuerySnapshot querySnapshot) {
querySnapshot.docs.forEach((doc) {
if (doc["email"] == signedInUser.email) {
name = doc['name'];
age = doc['age'];
sex = doc['sex'];
print(doc['name']);
}
});
});
}
This is my data
I want to retrieve then but i can’t because it says null how to fix the error?
this is the data I’m trying to retrieve
the error image

Please try this Code:
void getData() async {
await FirebaseFirestore.instance
.collection('users')
.get()
.then((value) {
for(var doc in value.docs) {
if (doc["email"] == signedInUser.email) {
name = doc.data()['name'];
age = doc.data()['age'];
sex = doc.data()['sex'];
print(doc.data()['name']);
}
}
});
}

Related

Make a stream wait for data to assign a late property

I have following code.
I need to assign a late userName (not stored in database BID table) from the Bid Object that i retreive from the firestore with a stream.
The userName can be found in another table in the database (USER table).
I need to connect those 2 all while a streambuilder is building with BID object stream coming in.
Repository
final _firestoreDB = FirebaseFirestore.instance;
Future<String> getDbUserNameFromDbUserID({required String dbUserID}) async {
try {
final docUser = _firestoreDB.collection('users').doc(dbUserID);
final snapshot = await docUser.get();
if (snapshot.exists) {
if (snapshot.data() != null){
return DbUser.fromJson(snapshot.data()!).userName;
}
}
throw Exception("getDBUserByDBUserId() No fireStore userName found");
} catch (e) {
throw Exception(e);
}
}
Stream<List<Bid>> getAllBidsByItemId({required String itemID}) {
try {
return _firestoreDB
.collection('bids')/*.orderBy('timestamp')*/
.where('itemID', isEqualTo: itemID)
.snapshots()
.map((snapshot) =>
snapshot.docs.map((doc) {
Bid bid = Bid.fromJson(doc.data());
bid.bidId = doc.id;
**bid.userName = await getDbUserNameFromDbUserID( dbUserID: bid.bidderID); ///????**
return bid;
}).toList());
} catch (e) {
throw Exception(e);
}
}
model
class Bid {
late String bidId;
**late String userName;**
final String bidderID;
final String itemID;
final double price;
final DateTime timestamp;
Bid(
{
required this.bidderID,
required this.itemID,
required this.price,
required this.timestamp});
Map<String, dynamic> toJson() => {
'bidderID': bidderID,
'itemID': itemID,
'price': price,
'timestamp': timestamp,
};
static Bid fromJson(Map<String, dynamic> json) => Bid(
bidderID: json['bidderID'],
itemID: json['itemID'],
price: json['price'],
timestamp: (json['timestamp'] as Timestamp).toDate(),
);
}
How can I assign the late String userName when the stream gets the objects from the firestore?
Whats the best practice to do this? I assume this is not the best way to go about it?
I am using Bloc and Firestore Firebase
Thank you
SOLUTION
Stream<List<Bid>> getAllBidsByItemId({required String itemID}) async* {
try {
yield* _firestoreDB
.collection('bids')
.where('itemID', isEqualTo: itemID)
.snapshots()
.asyncMap<List<Bid>>((event) async {
List<Bid> bids = [];
for (var doc in event.docs) {
try {
Bid bid = Bid.fromJson(doc.data());
bid.bidId = doc.id;
bid.userName = await getDbUserNameFromDbUserID( dbUserID: bid.bidderID);
bids.add(bid);
} catch (e) {
throw Exception(e);
}
}
return bids;
});
} catch (e) {
throw Exception(e);
}
}
This worked for me
SOLUTION
Stream<List<Bid>> getAllBidsByItemId({required String itemID}) async* {
try {
yield* _firestoreDB
.collection('bids')
.where('itemID', isEqualTo: itemID)
.snapshots()
.asyncMap<List<Bid>>((event) async {
List<Bid> bids = [];
for (var doc in event.docs) {
try {
Bid bid = Bid.fromJson(doc.data());
bid.bidId = doc.id;
bid.userName = await getDbUserNameFromDbUserID( dbUserID: bid.bidderID);
bids.add(bid);
} catch (e) {
throw Exception(e);
}
}
return bids;
});
} catch (e) {
throw Exception(e);
}}

My DocumentSnapshot type variable remains null despite the fact that I passed it to my function

I use flutter to retrieve a document from my firestore database. to do this, I create a DocumentSnapshot type variable which will be passed as a parameter to my getClasseName() function to retrieve the documents. Inside my function the document exists, so I assign it to my variable but my variable remains null when I want to use it
this is a snippet of my code
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:ude/model/enseignant.dart';
class DBServices {
static final CollectionReference lyceecol =
FirebaseFirestore.instance.collection('Lycee');
static Future getUser(String? lycee, String? id) async {
try {
final QuerySnapshot<Object?> snapshot = await lyceecol
.doc(lycee)
.collection('Enseignant')
.where('Id', isEqualTo: id)
.get();
final EnseignantM userData = snapshot.docs
.map((QueryDocumentSnapshot<Object?> e) => EnseignantM.fromSnapshot(
e as DocumentSnapshot<Map<String?, dynamic>>))
.single;
return userData;
} catch (e) {
print('ENSEIGNANT NOT FOUND');
print(e);
throw e;
}
}
static Future getClasseName(
String? lycee, String? id, List<String> list) async {
try {
String classeName;
int i = 0;
final snapshot = await lyceecol
.doc(lycee)
.collection('Enseignant')
.doc(id)
.collection('Classes')
.get();
for (var classe in snapshot.docs) {
DocumentReference<Map<String, dynamic>> classeRef =
classe.data()["reference"];
DocumentSnapshot<Map<String, dynamic>>? classRoom;
await DBServices.getClasse(classeRef.path, classRoom);
if (classRoom != null) {
print('CLASSENAME ${classRoom.data()!["Nom"]}');
classeName = classRoom.data()!["Nom"];
list[i] = classeName;
print(list[i]);
i++;
} else {
print('Impossible de charger les classes affilees a cet enseignant');
}
}
} catch (e) {
print(e);
rethrow;
}
}
static Future<void> getClasse(
String? path, DocumentSnapshot<Map<String, dynamic>>? classroom) async {
try {
await FirebaseFirestore.instance.doc(path!).get().then((snapshot) {
if (snapshot.exists) {
classroom = snapshot; //.data()!["Nom"];
} else {
debugPrint("Document not found");
}
});
debugPrint('CLASSROOMMMM: ${classroom!.data()!["Nom"]}');
} catch (e) {
debugPrint('CLASSE Not EXIST');
print(e);
rethrow;
}
}
}
---
/*this is where the problem is*/
static Future getClasseName(
String? lycee, String? id, List<String> list) async {
try {
String classeName;
int i = 0;
final snapshot = await lyceecol
.doc(lycee)
.collection('Enseignant')
.doc(id)
.collection('Classes')
.get();
for (var classe in snapshot.docs) {
DocumentReference<Map<String, dynamic>> classeRef =
classe.data()["reference"];
DocumentSnapshot<Map<String, dynamic>>? classRoom;
await DBServices.getClasse(classeRef.path, classRoom);
if (classRoom != null) {
print('CLASSENAME ${classRoom.data()!["Nom"]}');
classeName = classRoom.data()!["Nom"];
list[i] = classeName;
print(list[i]);
i++;
} else {
print('Impossible de charger les classes affilees a cet enseignant');
}
}
} catch (e) {
print(e);
rethrow;
}
}
static Future<void> getClasse(
String? path, DocumentSnapshot<Map<String, dynamic>>? classroom) async {
try {
await FirebaseFirestore.instance.doc(path!).get().then((snapshot) {
if (snapshot.exists) {
classroom = snapshot; //.data()!["Nom"];
} else {
debugPrint("Document not found");
}
});
debugPrint('CLASSROOMMMM: ${classroom!.data()!["Nom"]}');
} catch (e) {
debugPrint('CLASSE Not EXIST');
print(e);
rethrow;
}
}
I tried not to pass my variable as a parameter and to directly assign the Document returned by the function to it, but it didn't work.
Your help will be invaluable to me

Why is ChangeNotifier updating endlessly

I have 2 data provider classes that extend ChangeNotifier. Within each, there's a function to fetch data and at the end of them, I use notifyListeners() to notify the screens/listeners that the data changed. However, it seems that the listeners start getting notified endlessly instead of once and that creates a loop of reloading, circle indicators that don't go away, and a frozen screen. I don't get it.
Data providers:
class UsersDataProvider extends ChangeNotifier {
UsersDataProvider() : super();
static Map<int, QueryDocumentSnapshot<Object?>> usersMap = {};
Future<void> fetchUsers() async {
final userRef = FirebaseFirestore.instance.collection('users');
final QuerySnapshot result = await userRef.get();
final docs = result.docs.asMap();
usersMap = docs;
print(usersMap.length);
notifyListeners();
}
}
class PostsDataProvider extends ChangeNotifier {
PostsDataProvider() : super();
static Map<int, QueryDocumentSnapshot<Object?>> postsMap = {};
Future<void> fetchPosts() async {
UsersDataProvider.usersMap.forEach((index, resultValue) async {
final postsRef = FirebaseFirestore.instance
.collection('users')
.doc(resultValue.id)
.collection('posts');
final QuerySnapshot postsResult = await postsRef.get();
final postDocs = postsResult.docs.asMap();
postsMap = postDocs;
print('Post map: ${postsMap.length}');
notifyListeners();
});
}
}
Add listeners and reload data:
Future<void> fetchUsersAndPosts(bool initial) async {
if (!initial) {
setState(() {
postsLoading = true;
});
usersDataProvider.fetchUsers();
postsDataProvider.fetchPosts();
}
if (initial) {
usersDataProvider.addListener(() {
print('changed');
setState(() {
fetchUsersAndPosts(false);
});
});
}
if (initial) {
postsDataProvider.addListener(() {
setState(() {
fetchUsersAndPosts(false);
});
});
}
UsersDataProvider.usersMap.forEach((index, value) async {
List<Post> posts = [];
PostsDataProvider.postsMap.forEach((index, value) {
final post = Post.fromJson(value.data() as Map<String, dynamic>);
posts.add(post);
setState(() {});
if (posts.length == PostsDataProvider.postsMap.length) {
setState(() {
postsList = posts;
postsList.sort((a, b) {
return b.date.compareTo(a.date);
});
postsLoading = false;
});
}
});
final profileInfo =
ProfileInfoObject.fromJson(value.data() as Map<String, dynamic>);
Profile profile = Profile(profileInfo, postsList.where((p) => p.uid == value.id).toList());
UserSearchResult user = (UserSearchResult(profile, value.id));
if (usersList.where((u) => u.uid == user.uid).toList().isEmpty) {
setState(() {
usersList.add(user);
});
}
});
setState(() {
postsList.sort((a, b) {
return b.date.compareTo(a.date);
});
});
}

displaying only the current user data

I protected data_service with current user to only display the current user's habits.
data_service.dart:
class DataService {...
late final Database db;
Users? _user;
late final StreamData<Map<int, Habit>> habits;
Future<void> init() async {
db = await HabitsDb.connectToDb();
habits = StreamData(initialValue: await _getAllHabits(), broadcast: true);
}
String get userEmail => AuthService.firebase().currentUser!.email;
Future<Map<int, Habit>> _getAllHabits() async {
getOrCreateUser(email: userEmail); //issue
final habits = await _getAllHabitsFromDb();
final map = Map<int, Habit>();
final currentUser = _user;
print(currentUser);
for (final habit in habits) {
if (currentUser != null) {
print(currentUser.id);
print(habit.userId);
if (habit.userId == currentUser.id) {
map[habit.id] = habit;
}
}
//map[habit.userId] = currentUser?.id;
}
return map;
}
Future<List<Habit>> _getAllHabitsFromDb() async {
final habitsMap = await HabitsDb.getAllHabits(db);
final habitsList = habitsMap.map((e) => Habit.fromDb(e)).toList();
return habitsList;
}
Future<Users> getOrCreateUser({
required String email,
bool setAsCurrentUser = true,
}) async {
try {
//we found the user
final user = await getUser(email: email);
if (setAsCurrentUser) {
_user = user;
}
print(_user?.email);
return user;
} on CouldNotFindUser {
//we didn't find the user
final createdUser = await createUser(email: email);
if (setAsCurrentUser) {
_user = createdUser;
}
return createdUser;
} catch (e) {
rethrow;
}
}
...}
in main class:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
final dataService = DataService();
await dataService.init();
GetIt.I.registerSingleton(dataService);
... }
StreamData class:
class StreamData<T> {
List<Habit> _notes = [];
User? _user;
late final StreamController<T> _controller;
Stream<T> get stream => _controller.stream;
late T _value;
T get value => _value;
StreamData({required T initialValue, bool broadcast = true}) {
if (broadcast) {
_controller = StreamController<T>.broadcast();
} else {
_controller = StreamController<T>();
}
_value = initialValue;
}
the problem is that the line getOrCreateUser(email: userEmail); is only called once and it does not work when I switch user and I need to Hot Restart to fix it. I think using Futurebuilder will fix it. but if yes, how do I use it when there is a need to call dataService.init at the beginning of the main?
Since your getOrCreateUser function is declared as async, you'll want to use await when you call it in _getAllHabits:
await getOrCreateUser(email: userEmail)
This ensures the getOrCreateUser code has completed before the rest of the code in _getAllHabits (that depends on the result of getOrCreateUser) executes.

Flutter: LateError (LateInitializationError: Field 'user' has not been initialized.)

I am nit sure about this error because user should be inithialized in Auth Provider and then I will be able to use it in User Provider but flutter continue giving this error.
Here is my code. Can someone help to solve or tell me a better form to organize it?
AuthProvider
class AuthProvider extends ChangeNotifier {
late final FirebaseAuth _auth;
late final NavigationService _navigationService;
late final DatabaseService _databaseService;
late UserData user;
AuthProvider() {
_auth = FirebaseAuth.instance;
_navigationService = GetIt.instance.get<NavigationService>();
_databaseService = GetIt.instance<DatabaseService>();
_auth.authStateChanges().listen((_user) {
if (_user != null) {
//_databaseService.updateUserLastSeenTime(_user.uid);
_databaseService.getUser(_user.uid).then(
(_snapshot) {
if (_snapshot.exists) {
if (_snapshot.data() != null) {
user =
UserData.fromJson(jsonDecode(jsonEncode(_snapshot.data())));
notifyListeners();
}
}
_navigationService.removeAndNavigateToRoute('/home');
},
);
} else {
_navigationService.removeAndNavigateToRoute('/login');
}
});
}
User Provider
class UserProvider with ChangeNotifier {
final DatabaseService _databaseService = DatabaseService();
UserData _user = AuthProvider().user;
UserData get getUser => _user;
Future<void> refreshUser() async {
UserData user = await _databaseService.getUserDetails();
_user = user;
notifyListeners();
}
// update user name
Future<void> editName(String name) async {
try {
await _databaseService.getUserDoc(_user.uid).update({'name': name});
} catch (err) {
print(err.toString());
}
}
// update user last name
Future<void> editLastName(String lastName) async {
try {
await _databaseService
.getUserDoc(_user.uid)
.update({'lastName': lastName});
} catch (err) {
print(err.toString());
}
}
}