Rebuild FutureBuilder from another widget - flutter

Right now, I am using a FutureBuilder to build a list of activities. But the activities have a timer, using CountdownTimerController, which runs down to 0. I want to change the list and update the list onEnd of the timer. But the timer widget is on a different file overall. How could I do the needful?
FutureBuilder(
future: fetchUserActivities(),
builder: (context, snapshot) {
if (snapshot.connectionState ==
ConnectionState.done) {
if (snapshot.hasError) {
return Center(
.....
What should I change in this or add in OnEnd function of the timer to update the list?

you should embed your list in an other FutureBuilder inside the the one you use to fetch activities. Or put each list in a Statefull widget and call a setState by the end of your timer.
According to your comment, an easy way will be using a state management package like RiverPod, put your timer in a ChangeNotifier and use Consumer widget in your other page.

Related

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.

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 bloc streambuilder with refreshindicator rebuild twice

Widget build(BuildContext context) {
final blocData = WeatherBlocProvider.of(context).bloc;
if (WeatherBloc.permission == true) {
blocData.forceRefreshAll();
return Container(
child: StreamBuilder(
stream: blocData.zipAll,
builder: (scontext, snapshot){
//to do
}
now i am using bloc pattern with streambuilder
and when i refresh parent widget i can see blocData.forceRefreshAll() this line is requested twice.(i mean build method is requested twice) how can i make only one?
i saw unwanted rebuild subject and they said use instance or initstate but with bloc pattern i think using initstate is not possible and const value is not working with
blocData.forceRefreshAll()
build method is meant to build widget tree, it can be called multiple times for various reasons. This is why should not fetch data in build.
If you can't access bloc in initState because there is no context yet - override another method, didChangeDependencies. It's called right after initState and it can use context, so you can access bloc provider with it.

BLoCs and multiple streams - Is there a better solution?

Currently I'm working with BLoCs in Flutter and I've got a question about multiple streams within a Bloc.
For example when a screen has got multiple widgets which should depend on the Bloc. I could wrap the whole screen in the StreamBuilder, but then every time all widgets would be rebuilt.
The example bloc:
class TestBloc {
final StreamController _dataController = StreamController<String>();
final StreamController _appBarTitleController = StreamController<String>();
TestBloc();
Stream<String> get appBarTitle => _appBarTitleController.stream;
Stream<DataState> get data => _dataController.stream;
void fetchData(String path) async {
_dataController.sink.add(PokemonDataLoading());
Data data = await _getData();
_dataController.sink.add(Loaded(data));
_appBarTitleController.sink.add(data.name);
}
Future<Data> _getData(String path) async {
return await _dataRepository.fetchData(path);
}
void dispose() {
_dataController.close();
_appBarTitleController.close();
}
}
On the example build method you can see two different StreamBuilders, one for the app bar title and one for the content. Of course I could wrap them in this example into one StreamBuilder, but sometimes this isn't easily possible. They may depend on other data or user interactions.
#override
Widget build(BuildContext context) {
_testBloc.fetchData();
return ScaffoldWithSafeArea(
title: StreamBuilder(
stream: _testBloc.appBarTitle,
builder: (context, AsyncSnapshot<String> snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data);
}
return Text("Test");
},
),
child: StreamBuilder<DataState>(
stream: _testBloc.data,
builder: (context, AsyncSnapshot<DataState> snapshot) {
DataState state = snapshot.data;
if (state is DataInitial) {
return _buildLoading();
} else if (state is DataLoaded) {
return _buildContent(state.data);
}
return _buildLoading();
},
),
);
}
Is there maybe a better solution for multiple Streams on one screen? I use a lot of boilerplate code here and would like to avoid this.
In order to manage multiple streams in one screen, the best solution is to have multiple widgets that listen the corresponding stream.
This way, you increase the performance of your app by optimizing the total number of builds of your widgets.
By doing this, you can create widgets that listen an output (stream) of your BLoC and reuse them in different parts of your app, but in order to make the widget reusable you need to inject the BLoC into the widget.
If you see the BLoC UI design guidelines
Each "complex enough" component has a corresponding BLoC
This way your screen will now be composed of different components, and this component is a widget that listens to an output (stream) of your BLoC.
So you are doing things right.
If you want to reduce the repetitive code a bit in your widgets, you can:
Create your own widget that listens to the stream and directly returns the output of the BLoC (in your case you call state), this way you don't need to use snapshot.data like in the StreamBuilder. The example of this widget is the BlocBuilder of flutter bloc library.
Use the flutter bloc library that has widgets that reduce the boilerplate code when use BLoC pattern, but if you use this library, you now need to create your BLoCs using bloc library, but if you do this, now you reduce the boilerplate code of creating StreamControllers in your BLoC, and other interesting features, so you should take a look the power of bloc and flutter bloc libraries.

How to initialize and load data when moving to a screen in flutter?

I want to populate my lists by making API calls when moving to a screen in flutter. I tried calling the async function in the init state however it takes time to load the data of course, and that leads to a null error.
I tried using a future builder in my view. However, I want to make multiple API calls and I don't think that is possible with one future builder. And I would like to avoid having different future builders for each list.
Is there a neat way to do this?
Also is it advisable to load data and pass it on from the previous screen.. since I would like to avoid the loading time?
current code
FutureBuilder(
future: getAllData,
initialData: [],
builder: (context, snapshot) {
if (!snapshot.hasData || snapshot.data.isEmpty) return Center(child: CircularProgressIndicator(valueColor: new AlwaysStoppedAnimation<Color>(Colors.red),));
else {
return createNewTaskView(context, snapshot.data);
}
}),
init method
#override
void initState() {
this.getAllData = getSocieties();
}