I'm relatively new to Flutter and am currently struggling with FutureBuilders. I've read Remi's answer on this thread, which basically states Flutter's philosophy that widget builds should be idempotent. I get the principle in theory but how does that work practically though?
Consider the following snippet:
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Consumer<DataPresenter>(
builder: (context, presenter, _) {
return DefaultFutureBuilder<List<Data>>(
future: presenter.data(),
builder: (context, data) {
// bla bla
}
);
}
),
// More widgets
],
),
),
);
}
Where this is my DefaultFutureBuilder
:
class DefaultFutureBuilder<T> extends StatelessWidget {
final Future<T> future;
final Widget Function(BuildContext, T data) builder;
final Widget Function(BuildContext) errorBuilder;
DefaultFutureBuilder({
@required this.future,
@required this.builder,
this.errorBuilder,
});
@override
Widget build(BuildContext context) {
var errBuilder = errorBuilder ?? _buildDefaultErrorScreen;
return FutureBuilder<T>(
future: future,
builder: (context, snapshot) {
var indicator = Center(child: CircularProgressIndicator());
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasData)
return builder(context, snapshot.data);
else if (snapshot.hasError)
return errBuilder(context);
}
return indicator;
},
);
}
Widget _buildDefaultErrorScreen(BuildContext context) {
// default
}
}
Now I know that the call to presenter.data()
(fetches some async data) is not kosher. But I do want to re-fetch the data when the presenter notifies the consumer while at the same time I do not want to fetch it when Flutter rebuilds my widget tree because of framework shenanigans.
My initial idea was to build the presenter so that it only fetches the data when it does not have any data at the moment. I could then set the data to null from other methods and notify the listeners to rebuild the affected widget subtrees.
Like that:
class DataPresenter extends ChangeNotifier {
// fields, services, bla bla
List<Data> _data;
Future<List<Data>> data() async {
if (_data == null) {
var response = await _service.fetchMyStuff(params: params);
_data = response.data;
}
return _data;
}
}
The problem with this solution is that any rebuilds that happen while I fetch the data for the first time will cause another request.
I'd be grateful if someone could point out which part of the framework / architecture I didn't get.