How to set the object variable permanently in Flutter? - flutter

I am trying to build an app with Flutter and Dart. Here, I want to retrieve data from my Firebase, and I was able to do so and put it in the replies variable in each Message object that I have as an array type in the Firebase by using a for loop. However, when I try to access the replies variable again, it becomes empty.
I tried using setState, but that just causes the replies variable to keep resetting. Why this is the case, and how can I fix it?
StreamBuilder<List<Message>> pickMessage(
Stream<List<Message>> list, BuildContext context) {
return StreamBuilder(
stream: list,
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong!');
} else if (snapshot.hasData) {
final message = snapshot.data!;
for (var msg in message) {
FirebaseFirestore.instance.collection("messages").doc(msg.msgID).get().then((value){
msg.replies = value.get("replies");
});
}

If You Need Some Variable to Initialize Once Put in Init Function or In Builder Function, outside the Scope of StreamBuilder, hope it helps

Related

Flutter, updating variable values not appearing in application

After defining a variable ("xyz" at screenshot), I fetched some data on my firestore database, then changed that variable value to data i fetched from firestore. When I print the changed variable with "print()" it appears at "Run" the value I fetched, which is what I want. But when I run the app, the text I assigned as changed variable appears on the screen with old value like I never changed it after defining. the code
When I print(xyz); it appears as the data from firestore, so there is no problem at database connection. I just want to update the value appears at screen too.
you could also listen to those snapshots without using StreamBuilder or any Flutter widget with the listen() method like this:
final fieldValue = "initial value";
FirebaseFirestore.instance
.doc(documentPath)
.snapshots()
.listen((snapshot) {
final data = snapshot.data() as Map<String, dynamic>;
fieldValue = data["field"];
print(fieldValue);
});
the fieldValue will be updated now every time an update happens to the document, and it will print it automatically in the console with the print()
as I see in the screenshot, you're getting your document's data with a Future, and the Future is useful when you need to call data just once :
FirebaseFirestore.instance.doc(documentPath).get(); // Future
FutureBuilder<DocumentSnapshot>(
future: FirebaseFirestore.instance.doc(documentPath).get(),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasData) {
return Text("${snapshot.data!.data()}");
}
return Text("something else");
},
);
This will get the snapshot only once, even if you updated the document data, nothing will update on the screen until you make manually a new request to get another snapshot.
well the Firebase SDK for Flutter provide also Stream of snapshots for DocumentSnapshot and CollectionSnapshot, which listen directly to it, so whenever you have an update on your target, that stream will be notified and provide a new snapshot for that new updated data, and you can use it like this:
FirebaseFirestore.instance.doc(documentPath).snapshots(), // Stream
StreamBuilder<DocumentSnapshot>(
stream: FirebaseFirestore.instance.doc(documentPath).snapshots(), // Stream
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.active) {
final currentData = snapshot.data!.data() as Map<String, dynamic>;
final fieldValue = currentData["fieldString"];
return Text("$fieldValue");
}
return Text("something else");
},
);
change fieldString with your field name to get its value.
Now every update you do to the DocumentSnapshot, the StreamBuilder will get that new snapshot from the Stream and update the UI based on it.
Hope this helps!

How to listen for state changes inside a FutureBuilder widget and reflect the state change?

to demonstrate the problem, let me write down some code for a FutureBuilder.
FutureBuilder(future: _myFuture, builder: (context, snapshot) {
if(snapshot.hasData) {
// !!!! IMPORTANT !!!
// Pay attention to the _isFirstText variable below
return SizedBox(
child: _isFirstText ? Text(snapshot.data.firstText) : Text(snapshot.data.secondText),
);
}
if(snapshot.connectionState == ConnectionState.isWaiting) {
return Text('Waiting!');
}
return Text('Error');
}),
As I have mentioned in a comment in the above code, pay attention to the _isFirstText variable. Suppose that is a state variable. Inside the future builder, how do I get the correct return value that corresponds to the isFirstText state variable change.
I also came across this stack overflow post but could not get the code to work.
I also came across a widget called StatefulBuilder, but I can not figure out to where in my FutureBuilder I should use it.
Can someone please help?
If you want to listen to ongoing changes you can use a Streambuilder. Streams are not only for serverside changes but can also be used locally.
You can build a Stream yourself like this :
StreamController myStreamController = StreamController<int>();
To send a new event through this controller you can do
myStreamController.sink.add(newValue);
You can then listen to changes like this:
#override
Widget build(BuildContext context) {
return StreamBuilder<int>(
stream: myStreamController.stream,
builder: (context, snapshot) {
final value = snapshot.data;
return Text(value!.toString());
}
If you want to learn more check this Video: https://youtu.be/nQBpOIHE4eE
Let me know if this helped.
You can use ValueNotifier variables and use notifyListeners() to update a specific parts of your code Like this:
ValueListenableBuilder and use that ValueNotifier variable and listen to that.

Is there way to get a callback after StreamBuilder completely load?

I'm trying get a callback after my context build, i already tried with "WidgetsBinding.instance!.addPostFrameCallback", but not works because i have a StreamBuilder and the first load of snapshot data it is null.
I expected exists a callback of StreamBuilder after data completely load and build loaded, here is my code:
StreamBuilder(
stream: this.paymentInfoFormPresenter.key,
builder: (context, snapshot) {
final _formKey = snapshot.data;
if (_formKey == null) {
return Container();
}
return Text('example');
}
);
And i don't find anything about on internet... i hope someone can help me.
To resolve this issue, i needed change the "Text('example')" to an external widget, and added "WidgetsBinding.instance!.addPostFrameCallback" inside of this external widget

Using FutureBuilder inside Build method

I am using FutureBuilder inside my Build method and the FutureBuilder keeps firing up again and again. I only want to call FutureBuilder once until my future is loaded and then call another function as soon as it is done. This is my Build function -
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: parsings(),
builder:
(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return LoadingState(context);
default:
if (snapshot.hasError)
return new Text(
'Error in snapshot: \n${snapshot.error}');
else {
return func1();
}
}
},
);
}
As i said, my Build function keeps on building again and again.
Before this, i was using my future inside initState but because of that, my actual page that i wanted to return gave null values all over until the API was called. I dont want to see null values at all so i wanted to use FutureBuilder and display LoadingState() until the API was called and then show my page which has all the called values and doesnt show null values. Any help would be appreciated.
EDIT: The detailed issue i am facing is as the build function is being called again and again, i am seeing my LoadingState() again and again, that is, LoadingState() is appearing and then func1() is appearing then again, LoadingState(), then func1() and so on. this process does not stop at all. Also, i do not want to use parsings() in initState because on doing so, before the API is called, the func1() shows null values in my data and as soon as it is called, the build function changes its values to the values called from API. I don't want to see it like that which is why i wanted to use FutureBuilder.

Flutter Provider, where to place a function call to load data from firestore database

I'm currently working on an app and I want to get some data of a user's friends. Ideally, after sign in, this information is loaded up onto the screen for viewing.
I initially made my function call to do this within the "build" method of my HomeScreen, but this caused an infinite loop as the function call ended with "notifyListeners()".
That being said, where should I place my call to avoid a loop, while still having the UI update once the data has been fetched from the database?
You should have a look at the FutureBuilder widget, which provides great functionality to load async data and show the result once available. While the data is loading, or if your code runs into an error, you can show a different widget. Essentially it works like this:
FutureBuilder<String>(
future: _yourFuture,
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.active:
case ConnectionState.waiting:
// Return loading indicator
return Container();
case ConnectionState.done:
if (snapshot.hasError) {
// Return error
return Container();
} else if (snapshot.hasData) {
// Data loaded => use snapshot.data to access it
return Container();
}
}
})
I would not recommend to load data during initState(), since this method cannot be async and thus, you cannot await your result.
I hope that helps.