Retrieving firebase firestore documents in Flutter / Dart and Sound Null - firebase

I am working my way through a Udemy Flutter class and I am in a chapter dealing with Firebase. The class is about 3-4 years old and it seems is just old enough that the sample completed code crashes when accessing the Firebase portions. I started a new project from scratch and cobbled bits and pieces to get it mostly up and running but I have now hit a dead end. The new project and the firebase plugins are Sound Null and the class code is not. Getting and printing data from the database worked fine until I tried getting it hooked up to a stream. I am specifically running into a problem iterating over the received documents.
When I set up the message variable using (the Flutter) snapshot.data in a for-in loop and try to iterate over the returned documents (#1) I have a null problem. Without specifying the type (#1a) as AsyncSnapshot<dynamic> the for-in loop errors that I cannot iterate over a non nullable.
Changing the type gets rid of the compile time error but generates a runtime error of
Type _JsonQuerySnapshot is not a subtype of type Iterable
Dart is not my primary language and I have been pulling my hair out googling this for several hours to no avail. Any help is appreciated.
final _firestore = FirebaseFirestore.instance; //<-----earlier in the code
children: <Widget>[
StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) { //<----Problem #1a here
List<Text> messageWidgets = [];
if (snapshot.hasData) {
final messages = snapshot.data;
for (var message in messages) { //<----------Problem #1 here
final messageText = message.data['text'];
final messageSender = message.data['sender'];
final messageWidget =
Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);
}
}
return Column(
children: messageWidgets,
);
},

Well, after another hour or so of googling around I found the right place to look. The docs for the flutter plugin for firestore have a massive difference from the course I am taking. Reading(ctrl-c, ctrl-v) them I'm at least able to mostly understand and have the program back on track to finish my course.

Related

Realtime data from firestore flutter not working

I was following this tutorial on how to get realtime updates from flutter firestore, https://medium.com/firebase-tips-tricks/how-to-use-cloud-firestore-in-flutter-9ea80593ca40 and I scrolled down to Listen For Realtime Updates section and when I followed the tutorial, this is what I came up with,
String name = 'name here';
String children = 'children here';
String docId = '0';
#override
void initState() {
getUsers();
super.initState();
}
getUsers() async {
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
final uid = user!.uid;
FirebaseFirestore.instance
.collection("userNames")
.where("uid", isEqualTo: uid)
.snapshots()
.listen((result) {
result.docs.forEach((result) {
print(result["firstName"]);
print(result["children"].toString());
name = result["firstName"];
children = result["children"].toString();
});
});
}
When I print the values to the console they update in realtime, but when I put them in variables and concatenate them into a Text widget like this:
Text('Children: $children'), //It does not update in realtime.
For instance, if in my document if I have children: 3 and I display in in my app, it shows 3, but when I manually change it, it does not update in realtime, I have to press hot reload. Another issue is that I have to initialize the variable before using them in the function, up ahead in the first 3 lines of code. When I hot restart, it shows the values of what I use to initialize them. For where it should show children, it says 'children here' and for where the name is, it puts 'name here', only when I hot reload the page, do the actual firestore values get inputed into them and show data from the firestore database. If there is a solution to any of these problems, I would much prefer an answer in code instead of a link or a brief explanation, I spend hours before I find a piece of code that utilizes the explanation. Thank you
I use snapshots().listen() to listen to change. Then I use ValueNotifier to notify the UI.
final itemsNotifier = ValueNotifier<List<Item>>([]);
FirebaseFirestore.instance
.collection("userNames")
.where("uid", isEqualTo: uid)
.snapshots()
.listen((event) {
itemsNotifier.value = event.docs
.map((doc) => Item.fromSnapshot(
doc as DocumentSnapshot<Map<String, dynamic>>))
.toList();
itemsNotifier.notifyListeners();
});
Since the data is loaded asynchronously, the data isn't available when Flutter first paints your Text widget. You'll need to tell Flutter that it has to repaint the UI when the data is available.
There are two common ways to do this:
Put the children variable in the state of your widget by calling setState(). This will tell Flutter to repaint the widget, and your text will then show the value.
You can also use a StreamBuilder widget, which does the above too - but it also handles all kinds of error states automatically.
I recommend reading about stateful widgets and setState and about the StreamBuilder class to learn more.

snapshot returns always empty data in StreamBuilder flutter

So, I'm facing this problem: the 'snapshot' doesn't get any data from Firestore in StreamBuilder in Flutter.$
Here is the code:
StreamBuilder<Driver>(
initialData: null,
stream: DatabaseServices(uid: driver.uid).driverData,
builder: (streamContext, snapshot) {
print(driver.uid);
if (snapshot.hasData) {
Driver currentDriver = snapshot.data;
print(currentDriver.fullName);
print(currentDriver.email);
} else {
print('no data');
}
}
)
Note: stream: DatabaseServices(uid: driver.uid).driverData
-> driver here works fine on top of the whole code and gets the driver data such as uid.
And this code always returns 'no data'.
The weird thing here is that I'm using the same code (with another kind of user -> Client) in another screen, and it works normally, and it gets the data properly.
And in Firestore, I have 2 collections, Driver and Clients, almost the same attributes.
It even has a SubCollection for both collections and it called 'Notification', and I'm using a StreamBuilder to show the notifications for both Client and Driver and it works normally.
Problem solved, the problem was in some attribute that I called it with the wrong name that used in the other collection (Client), I forgot to change it.

Not getting data in latest firestore flutter library

While implementing the latest firestore library for flutter project, I am getting the the below error
Bad state: field does not exist within the DocumentSnapshotPlatform
CODE IMPLEMENTED
return ListView.builder(
itemCount: snapshot.data.docs.length,
itemBuilder: (context, index){
String itemTitle = snapshot.data.docs[index]['postContent'];
return ContentList(postContent: postContent);
});
Please guide me how to resolve, I am using Firestore ^0.14.3 dependency
The error is on this line String itemTitle = snapshot.data.docs[index]['postContent']; where Flutter tries to look for an item in the map with the key of 'postContent' but it is not found.
It is up to you to figure out why this so but I would also like refrain against asking questions which have already been asked. Next time just a tip, paste the error into Google and review the top links. :)
Below is a duplicate:
https://github.com/FirebaseExtended/flutterfire/issues/3826

Cloud firestore read count in StreamBuilder

Firebase structure:
Code:
I'm using a StreamBuilder for document uid like this:
#override
Widget build(BuildContext context) {
return StreamBuilder<User>(
stream: _stream(),
builder: (BuildContext _, AsyncSnapshot<User> snapshot) {
// this block may get called several times because of `build` function
if (snapshot.hasData) {
final user = snapshot.data;
return SomeWidget(user: user);
}
return CircularProgressIndicator();
},
);
}
Questions:
Since StreamBuilder's builder may get called several times because of the build() method, will that cost me a read every time builder gets called?
Is there any difference in terms of read-count when reading complete uid vs reading uid/education?
If I update age and name value, will that count as one-write or two-writes in terms of firebase write-count?
Firestore charges on every document read, write and delete therefore:
Since StreamBuilder's builder may get called several times because of the build() method, will that cost me a read every time builder gets called?
Yes, if you are reading(retrieving) one document each time, then you will be charged as one read.
Is there any difference in terms of read-count when reading complete uid vs reading uid/education
No difference. The read is done in the document, when you retrieve one document then you are doing one read.
If I update age and name value, will that count as one-write or two-writes in terms of firebase write-count?
If you update one document once (even if all the fields are updated), it will cost you one write operation.

FutureBuilder doesn't work without touching the screen

I have the following situation: there's a button on the screen, which adds data to db. And there's the future builder which has future getDataFromDB. When I add data by button, the future builder doesn't get data from DB. And when I do several swipes on the screen it works correctly. What's the matter? Here's the code:
FutureBuilder(
future: DatabaseManager().findAllCaloriesForSelectedDate(currentDate),
builder: (context, AsyncSnapshot<int> snapshot) {
if (snapshot.hasData) {
_caloriesCurrent = snapshot.data;
return AnimatedCircularChart(
size: Size(constraints.maxWidth * 0.8, constraints.maxWidth * 0.8),
initialChartData: <CircularStackEntry>[
CircularStackEntry(
<CircularSegmentEntry>[
CircularSegmentEntry(
currentProgress,
Color(AppColors.brandViolet),
),
CircularSegmentEntry(
100 - currentProgress,
Color(AppColors.layoutBackgroundColor),
),
],
),
],
chartType: CircularChartType.Radial,
edgeStyle: SegmentEdgeStyle.round,
percentageValues: true,
);
} else {
return Container();
}
},
)
I'll appreciate any help. Thanks in advance!
There're two problems with your code:
You're obtaining Future object within build function which isn't right way to do it. Every time your widget rebuilds, you get new Future object (so, maybe, it rebuilds when you touch/scroll). You should store Future object in State of your Widget.
The future must have been obtained earlier, e.g. during State.initState, State.didUpdateConfig, or State.didChangeDependencies. It must not be created during the State.build or StatelessWidget.build method call when constructing the FutureBuilder.
You're using FutureBuilder, but you expect FutureBuilder to rebuild after you change some data. FutureBuilder doesn't work like that and rebuilds only once - when Future value is resolved (if you store Future object within State. In your case, it always gets new Future on rebuild). You probably want to use StreamBuilder. It will allow you to add new data to the stream of data. StreamBuilder will trigger on stream changes and automatically rebuild.
You should read about BloC Architecture, in case you haven't.

Resources