Problem pulling data from GetIt to stream builder - android

Error text:
Null check operator used on a null value The relevant error-causing
widget was StreamBuilder<List>
ChangeNotifierProvider(
create: (context) => getIt<ModelCreator>(),
child: StreamBuilder(
stream: getIt<ModelCreator>().chatList(),
builder: (context, AsyncSnapshot<List<ChatModel>> snapshot) {
var _data = snapshot.data!; <- The error is here
return Container();
}),
),
There is no problem with empty Container, that part is full.
Thats getChatList() func
Stream<List<ChatModel>> getChatList() {
var data = _firebase
.collection('Users')
.doc(_fireauth.currentUser!.uid)
.collection('Chat List')
.orderBy('Last Message Date', descending: true);
return data.snapshots().map(
(event) => event.docs.map((e) => ChatModel.fromSnapshot(e)).toList());
}
Thats it ChatModel
factory ChatModel.fromSnapshot(
QueryDocumentSnapshot<Map<String, dynamic>> doc) {
return ChatModel(
id: doc.id,
image: doc['Image'],
phone: doc['Phone'],
photo: doc['Photo'],
username: doc['Username'],
createdDate: doc['Created Date'],
displayMessage: doc['Display Message'],
lastMessageDate: doc['Last Message Date'],
);
}

the snapshot data can be null. So before accessing the snapshot.data you should check
if(snapshot.hasData)
if it returns true, you can access the data without any issues

Related

Null check operator used on a null value on Flutter 2.5.3

i´m getting the error above on flutter version 2.5.3, and it is happening when i try to logout. Apparently the error has something to do with the Products provider, just like the error is showing. But i´m still not able to fix it. It may also have something to do with null safety, and since i´m not very acquainted to it, i might be missig something.
> The following _CastError was thrown building _InheritedProviderScope<Products?>(dirty, dependencies: [_InheritedProviderScope<Auth?>], value: Instance of 'Products',
> listening to value):
> Null check operator used on a null value
The relevant error-causing widget was ChangeNotifierProxyProvider<Auth, Products>
main.dart
return MultiProvider(
providers: [
ChangeNotifierProvider(create: (context) => Auth()),
ChangeNotifierProxyProvider<Auth, Products>(
update: (context, auth, previousProducts) => Products(auth.token!, auth.userId,
previousProducts == null ? [] : previousProducts.items),
create: (_) => Products('', '', []),
),
ChangeNotifierProvider(create: (context) => Cart()),
ChangeNotifierProxyProvider<Auth, Orders>(
update: (context, auth, previousOrders) => Orders(auth.token!,
previousOrders == null ? [] : previousOrders.orders, auth.userId),
create: (_) => Orders('', [], ''),
),
],
child: Consumer<Auth>(
builder: (context, authData, child) => MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
fontFamily: 'Lato',
colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.purple)
.copyWith(secondary: Colors.deepOrange),
),
home: authData.isAuth ? ProductsOverviewScreen() : AuthScreen(),
//routes
),
));
auth.dart
class Auth with ChangeNotifier {
String? _token;
DateTime? _expiryDate;
String? _userId;
bool get isAuth {
return token != null;
}
String get userId {
return _userId!;
}
String? get token {
if (_expiryDate != null &&
_expiryDate!.isAfter(DateTime.now()) &&
_token != null) {
return _token;
}
return null;
}
Future<void> _authenticate(
String email, String password, String urlSegment) async {
final url = Uri.parse(
"https://identitytoolkit.googleapis.com/v1/accounts:$urlSegment?key=AIzaSyAA9PShE7c2ogk5L13kI0mgw24HKqL72Vc");
try {
final response = await http.post(url,
body: json.encode({
'email': email,
'password': password,
'returnSecureToken': true
}));
final responseData = json.decode(response.body);
if (responseData['error'] != null) {
throw HttpException(responseData['error']['message']);
}
_token = responseData['idToken'];
_userId = responseData['localId'];
_expiryDate = DateTime.now()
.add(Duration(seconds: int.parse(responseData['expiresIn'])));
} catch (error) {
throw error;
}
notifyListeners();
}
Future<void> logout() async {
_token = null;
_userId = null;
_expiryDate = null;
notifyListeners();
}
}
Problem:
This issue was actually caused by the null check operator (!) added to the non-nullable String variable auth.token in your main.dart file, which basically promises Dart that the value of auth.token would never be null, and that promise was not kept because the _token variable in the logout() method in your auth.dart file was set to null and your _token is actually passed to your token getter that you call using auth.token! in your main.dart file.
Solution:
You can of course easily fix this by removing the null check operator from the auth.token variable in your main.dart file and setting it to an empty String if it's value was equal to null, like this:
ChangeNotifierProxyProvider<Auth, Products>(
create: (_) => Products('', '', []),
update: (ctx, auth, previousProducts) => Products(
auth.token ?? '',
auth.userId ?? '',
previousProducts == null ? [] : previousProducts.items,
),
),
you should also make your userId getter nullable like this or it would throw another error:
String? get userId {
return _userId;
}
the ?? operator in Dart is basically an assignment operator that executes when the variable it is used for is equal to null.
And here's an extra guide from the Dart Team on working with null safety: https://dart.dev/null-safety

Query a specific document inside 2 streamBuilder

I created 2 different collections (users and follow).
Now I want to:
Fetch document which exist inside the follow's collection("list") according by their id.
Fetch users data in the first collection where currentUid = doc.id (for the second collection).
Display data in the ListTile.
First collection
await FirebaseFirestore.instance.collection("users").doc(currentUid).set({"name":username,"photoUrl":url,"uid":currentUid});
Second collection
await FirebaseFirestore.instance.collection("follow").doc(currentUid).collection("list").doc(otherId);
I used this but it doesn't work properly
body:StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("follow")
.doc(user!.uid)
.collection("list")
.snapshots(),
builder: (context, snapshot1) {
if (!snapshot1.hasData) {
return Container();
} else {
return StreamBuilder<QuerySnapshot>(
stream: FirebaseFirestore.instance
.collection("users")
.where("uid",
isEqualTo:
snapshot1.data!.docs.map((e) => e.id).toList())
.snapshots(),
builder: (context, snapshot2) {
if (!snapshot2.hasData) {
return Container();
} else {
return ListView(
children: snapshot2.data!.docs.map((e) {
return ListTile(
leading: CircleAvatar(
backgroundImage: NetworkImage(e.get('url')),
radius: 30,
),
title: Text(e.get('username')),
);
}).toList(),
);
}
},
);
You should use the in query instead of the equal to query.
An in query returns documents where the given field matches any of the
comparison values.
The syntax for the in query in the cloud_firestore is this:
.where(field, whereIn: listOfFields)
Solution:
Change this:
.where("uid", isEqualTo: snapshot1.data!.docs.map((e) => e.id).toList())
to this:
.where("uid", whereIn: snapshot1.data!.docs.map((e) => e.id).toList())

I'm facing a error The method '[]' was called on null. Receiver: null Tried calling: []("postId")

#override
Widget build(BuildContext context) {
return FutureBuilder(
future: postsRef
.document(userId)
.collection("usersPosts")
.document(postId)
.get(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
Post post = Post.fromDocument(snapshot.data);
return Center(
child: Scaffold(
appBar: header(context, titleText: post.description),
body: ListView(
children: [
Container(
child: post,
)
],
),
),
);
},
);
}
also this my error
The following NoSuchMethodError was thrown building FutureBuilder<DocumentSnapshot>(dirty, state: _FutureBuilderState<DocumentSnapshot>#37f30):
The method '[]' was called on null.
Receiver: null
Tried calling: []("postId")
The relevant error-causing widget was:
FutureBuilder<DocumentSnapshot> file:///D:/Flutter/KadShare/kadshare/lib/pages/post_screen.dart:18:12
When the exception was thrown, this was the stack:
#0 Object.noSuchMethod (dart:core-patch/object_patch.dart:54:5)
#1 DocumentSnapshot.[] (package:cloud_firestore/src/document_snapshot.dart:29:42)
#2 new Post.fromDocument (package:kadshare/widgets/post.dart:34:18)
#3 PostScreen.build.<anonymous closure> (package:kadshare/pages/post_screen.dart:28:26)
#4 _FutureBuilderState.build (package:flutter/src/widgets/async.dart:773:55)
My Post Class
final String postId;
final String ownerId;
final String username;
final String location;
final String description;
final String mediaUrl;
final dynamic likes;
Post({
this.postId,
this.ownerId,
this.username,
this.location,
this.description,
this.mediaUrl,
this.likes,
});
factory Post.fromDocument(DocumentSnapshot doc) {
return Post(
postId: doc["postId"],
ownerId: doc["ownerId"],
username: doc["username"],
location: doc["location"],
description: doc["description"],
mediaUrl: doc["mediaUrl"],
likes: doc["likes"],
);
}
Your snapshot is a Flutter AsyncSnapshot, specifically a AsyncSnapshot<DocumentSnapshot>. When snapshot.hasData is true, that means the DocumentSnapshot exists.
But a DocumentSnapshot exists even when the underlying document doesn't exist in the database, so you also need to check if the DocumentSnapshot has data, which you do with DocumentSnapshot.exists.
So your check then becomes:
if (!snapshot.hasData && snapshot.data.exists) {
So this change means the spinner will keep being rendered if the document doesn't exist.
Alternatively, you may want to render a different UI if the document doesn't exist:
builder: (context, snapshot) {
if (!snapshot.hasData) {
return circularProgress();
}
if (!snapshot.data.exists) {
return Text("Document does not exist");
}
Post post = Post.fromDocument(snapshot.data);
return Center(
child: Scaffold(
appBar: header(context, titleText: post.description),
body: ListView(
children: [
Container(
child: post,
)
],
),
),
);
},
Also see What is the difference between existing types of snapshots in Firebase?
Your snapshot.data is null;
Maybe change:
if (!snapshot.hasData || snapshot.data == null) {
i assume you are working on fluttershare course app, in your activity feed page when you press show post function make sure you give userId your currentUser.id passing in a non-null value for the 'postId' parameter as shown here:
showPost(context) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => PostScreen(
userId: currentUser.id,
postId: postId,
),
),
);
}

Flutter nested firebase istances

Can someone explain me why the productlist variable is null when this class run? It loooks like the variable is being populated after the first istance end, so the CardShopList widget hasn't the productlist that is coming from the nested istance.
Have you got some tips? Thanks!
Future getMyShopLists(User user) async {
List<Widget> listItems = [];
FirebaseFirestore.instance
.collection('user')
.doc(user.uid)
.collection('shoplist')
.get()
.then((event) {
event.docs.forEach((shoplist) {
List<ProductModel> productlist = [];
Map shopListData = shoplist.data();
shoplist.reference.collection('productlist').get().then((element) {
Map productData = shoplist.data();
element.docs.forEach((doc) {
productlist.add(ProductModel(
id: doc.id,
name: productData['name'],
quantity: productData['quantity'],
price: productData['price'],
));
});
});
listItems.add(CardShopList(
shoplist.id,
shopListData['name'],
productlist, // <------------------------------- THIS IS NULL!!!!
getMyShopLists,
key: ValueKey<String>(shoplist.id),
));
});
if (this.mounted) {
setState(() {
shopListsWidget = listItems;
});
}
});
return shopListsWidget;
}
EDIT i use that class in this FutureBuilder :
FutureBuilder(
future:
searchIcon.icon == Icons.search ? getMyShopLists(user) : null,
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.hasData) {
return isShopListEmpty
? new Center(
child: new Text(
"x",
style: TextStyle(fontSize: 20),
textAlign: TextAlign.center,
),
)
: Container(
child: Column(
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: shopListsWidget.length,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return shopListsWidget[index];
}))
],
),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
The "productlist" is not expected to be filled because you are using foreach. There may be alternative solutions, but turning foreach to for will probably fix it.
...
await FirebaseFirestore.instance //added await
.collection('user')
.doc(user.uid)
.collection('shoplist')
.get()
.then((event) async{ //added async
for(QueryDocumentSnapshot shoplist in event.docs){ //---this line changed
List<ProductModel> productlist = [];
Map shopListData = shoplist.data();
await shoplist.reference.collection('productlist').get().then((element) //added await
{
Map productData = shoplist.data();
for(QueryDocumentSnapshot doc in element.docs) //---this line changed
{
productlist.add(ProductModel(
id: doc.id,
name: productData['name'],
quantity: productData['quantity'],
price: productData['price'],
));
}
});
listItems.add(CardShopList(
shoplist.id,
shopListData['name'],
productlist,
getMyShopLists,
key: ValueKey<String>(shoplist.id),
));
}
...
Keep in mind I did not tested this, but I had previous issues with that.
My suggestion is to move this block of code:
listItems.add(CardShopList(
shoplist.id,
shopListData['name'],
productlist, // <------------------------------- THIS IS NULL!!!!
getMyShopLists,
key: ValueKey<String>(shoplist.id),
));
Inside the firebase query like so:
shoplist.reference.collection('productlist').get().then((element) {
Map productData = shoplist.data();
element.docs.forEach((doc) {
productlist.add(ProductModel(
id: doc.id,
name: productData['name'],
quantity: productData['quantity'],
price: productData['price'],
));
});
listItems.add(CardShopList(
shoplist.id,
shopListData['name'],
productlist,
getMyShopLists,
key: ValueKey<String>(shoplist.id),
));
});
The reason for the null value is that the firebase query has a local variables and they exist only for the moment of the request. The only way to work with them is locally in the query callback.
You're defining it as an async function, but didn't await for the queries to be processed in order, can you try adding await in these locations:
//here #1
await FirebaseFirestore.instance
.collection('user')
.doc(user.uid)
.collection('shoplist')
.get()
.then((event) async {
event.docs.forEach((shoplist) {
List<ProductModel> productlist = [];
Map shopListData = shoplist.data();
//here#2
await shoplist.reference.collection('productlist').get().then((element) {
Map productData = shoplist.data();
element.docs.forEach((doc) {
productlist.add(ProductModel(
id: doc.id,
name: productData['name'],
quantity: productData['quantity'],
price: productData['price'],
));
});
});

How to retrieve the single document data and show it in app?

Here, I'm trying to print the username of the user and it produces the following error.
I created a function userData() and the code is
userData() async
{
var userData = await _db.collection('users').document(_uuid).get();
return userData;
}
Where I'll just get the data of the specific user using uid and returning it.
The code I used to print the data is as follow
return FutureBuilder(
future: authService.userData(),
builder: (_,snapshots){
if(snapshots.connectionState == ConnectionState.waiting)
{
return Center(child: Text('Loading...'),);
}
else{
print("Snapshots: "+snapshots.toString());
return ListView.builder(itemCount: 1, itemBuilder: (_,index){
return ListTile(
title: Text(snapshots.data["username"].toString()),
);
});
}
}
);
The structure of firestore is
Collection('user') -> document(uid) -> 1.username 2.email ....
What is the reason for this error and how to resolve it?
The error message is telling you that snapshots.data is null, so you can't index into it with the [] operator.
Before using a DocumentSnapshot, you should check to see if it exists before accessing its field data. The snapshot's data property will be null if the document doesn't exist.
For Firestore I would recommend using Stream Builder rather than future Builder.
return StreamBuilder(
stream: Firestore.instance
.collection('user')
.document(uid)
.snapshots(),
builder: (_,snapshot){
if(!snapshot.hasData)
{
return Center(child: Text('Loading...'),);
}
else{
return ListView.builder(itemCount: 1, itemBuilder: (_,index){
return ListTile(
title: Text(snapshots.data["username"].toString()),
);
});
}
}
);

Categories

Resources