I have a problem here with flutter (v2.8.1) on my windows.
I am trying to use StreamBuilder class to fetch data from firebase but it is not working anyhow. I tried to use BuildContext context but it is still throwing me error on context.
Please have a look at my code and let me know what I am doing wrong. Answers are appreciated. Thanks in advance.
StreamBuilder(builder: (BuildContext context, snapshot), stream: _firestore.collection('messages').snapshots()),
Error :
The argument type 'Type' can't be assigned to the parameter type 'Widget Function(BuildContext, AsyncSnapshot<QuerySnapshot<Map<String, dynamic>>>)'.
Please check the image : https://imgur.com/a/QJs6hS9
The builder argument should be a function that returns a widget.
builder: (context, snapshot) {
// return a widget that uses the snapshot
// for example
return Text(snapshot.data().title);
}
Related
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.
With reference to the recent changes regarding FirebaseUser to User. FirebaseAuth.instance.currentUser() is not found at all (while throughing error "The expression doesn't evaluate to a function, so it can't be invoked." The solution to that however was to simply remove the paranthesis as FirebaseAuth.instance.currentUser. But now we have another error that it isn't a future type i.e "The argument type User can't be assigned to the parameter type Future<Object?>?". Following is my code block.
return FutureBuilder(
future: FirebaseAuth.instance.currentUser,
builder: (ctx, futureSnapshot) => ListView.builder(
reverse: true, // So that 1st message goes to last.
itemCount: chatDocument.length,
itemBuilder: (ctx, index) => MessageBubble(
message: chatDocument[index]['text'],
isMe: chatDocument[index]['userId'],
),
),
);
In the above code block I intend to provide future to my `FutureBuilder`. In summary previously `FirebaseUser` object did return a future, but probably `User` doesn't anymore. How may I handle this case? Thanks in advance.
I don't typically do that with a FutureBuilder. Once you have a user, you don't need to async it.
final FirebaseAuth_auth = FirebaseAuth.instance();
final User? user;
user = _auth.currentUser;
Then, if user != null ....create your ListView else, return a CircularProgressIndicator or whatever.
Look up Net Ninja for some nice videos for getting yourself set up with all that, just having a stream based on userChanges() for your project. More robust setup.
This question already has an answer here:
How to create a StreamProvider and subscribe to it later, Flutter
(1 answer)
Closed 1 year ago.
I have a stream provider in my Flutter app which fetches data from my Firestore database.
I want to show a loading widget while loading the data until it is done loading. Similar to the Futurebuilder with the ConnectionState function for example.
Is this possible?
Thank you for help!
When you use a future builder you can check if your snapshot.data is null. If it is null you show the spinner, otherwise you show your content:
StreamBuilder(
stream: myStream, //YOUR STREAM
builder: (BuildContext context, AsyncSnapshot snapshot) {
return snapshot.data == null
? Spinner() //LOADING SPINNER
: Container() //AFTER RETRIEVING CONTENT
}
)
UPDATE
Since you mentioned Future builder I got mislead. This is the answer you are searching for:
How to create a StreamProvider and subscribe to it later, Flutter
When the app starts it throws an Exception caught by widgets library in a console:
======== Exception caught by widgets library ======================================================= The following NoSuchMethodError was thrown building
StreamBuilder<List<Entry>>(dirty, state:
_StreamBuilderBaseState<List<Entry>, AsyncSnapshot<List<Entry>>>#97504): The getter 'length' was called on
null. Receiver: null Tried calling: length on `itemCount: snapshot.data.length`,
But the whole app nonetheless works in emulator. What is the reason of that?
StreamBuilder<List<Entry>>(
stream: entryProvider.entries,
builder: (context, snapshot) {
return ListView.builder(
itemCount: snapshot.data.length, // the error is here
itemBuilder: (context, index) { ...
When the stream is busy loading data, snapshot.data will be null until the ConnectionState of StreamBuilder is ConnectionState.done.
There are a few fixes you can try.
1. Use if (snapshot.hasData)
This will ensure that snapshot.data is used only when it is not null
2. Use Null operator
When trying to get the length of snapshot.data, try this
snapshot?.data.length ?? 0
3. Check ConnectionState
You can also check the ConnectionState of StreamBuilder but you might still need to use the first solution i.e, snapshot.hasData, normally I
would prefer one of the above solutions.
Use snapshot.data.size instead of .length.
solved the problem by replacing StreamBuilder<Object> with StreamBuilder<QuerySnapshot>. by default the StreamBuilder comes in this form StreamBuilder<Object>
this will work 100%
So in my build function, I load my user data, and once it is loaded I would like to change the theme of the app (which calls setState()). The issue is that I can't call setState during the build process, like the error below states. How would I go about loading a user selected theme on app startup? Also, is it not recommended to be loading data inside the build function? It seems to work well but feels kinda gross. Thanks!
Widget build(BuildContext context) {
//get user object from Firebase
//once user is loaded, take their chosen color theme and call updateTheme()
}
Error:
This ThemeSwitcherWidget widget cannot be marked as needing to build because the framework is
I/flutter (23889): already in the process of building widgets. A widget can be marked as needing to be built during the
I/flutter (23889): build phase only if one of its ancestors is currently building. This exception is allowed because
I/flutter (23889): the framework builds parent widgets before children, which means a dirty descendant will always be
For loading data or doing a blocking call, you should use FutureBuilder or StreamBuilder (I am not much aware of firebase APIs so can't tell which one to use, but both of them are very similar.) It takes a future or stream as an argument and builds based on it. I am assuming you know about future API of dart
Here is some example code that will give you some idea.
StreamBuilder<FirebaseUser>(
stream: FirebaseAuth.instance.onAuthStateChanged,
builder: (BuildContext context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return new SplashScreen();
} else {
if (snapshot.hasData) {
return new MainScreen(firestore: firestore, uuid: snapshot.data.uid);
}
return new LoginScreen();
}
}
)