Reputation: 1747
I'm trying to set State based on result received by FutureBuilder, looks like it is not possible since FutureBuild is still building, any ideas please ?
error :
The following assertion was thrown building FutureBuilder<String>(dirty, state: _FutureBuilderState<String>#db9f1):
setState() or markNeedsBuild() called during build.
This StatefulBuilder widget cannot be marked as needing to build because the framework is already in the process of building widgets. A widget can be marked as needing to be built during the build phase only if one of its ancestors is currently building. This exception is allowed because the framework builds parent widgets before children, which means a dirty descendant will always be built. Otherwise, the framework might not visit this widget during this build phase.
The widget on which setState() or markNeedsBuild() was called was: StatefulBuilder
my code :
FutureBuilder<String>(
future: fetchIdPlayer(idPlayer, bouquet),
builder: (context, snapid) {
if (!snapid.hasData)
return Container(
height: mobileHeight * 0.05,
child: Center(
child: CircularProgressIndicator(),
),
);
else if (snapid.data == "Error_ID") {
setState(() {
have_ID = true;
resultName = "رقم تعريف اللاعب خاطئ";
});
}
})
Upvotes: 2
Views: 2361
Reputation: 564
Flutter prohibit this kind of situation because setting a state in side a future widget will cause to rebuild the future widget again and this will become an infinite loop. Try to separate logic from UI. Example credits goes to @pskink and @Mofidul Islam
In your state class create a wrapper
Future<String> wrapper(idPlayer, bouquet) async {
final foo = await fetchIdPlayer(idPlayer, bouquet);
//handle errors
if(foo.error){
have_ID = true;
resultName = "رقم تعريف اللاعب خاطئ";
}
return foo;
}
call the wrapper in your widget
FutureBuilder<String>(
future: wrapper(idPlayer, bouquet),
builder: (context, snapid) {
if (!snapid.hasData)
return Container(
height: mobileHeight * 0.05,
child: Center(
child: CircularProgressIndicator(),
),
);
else {
//UI to show error
}
})
Upvotes: 2
Reputation: 4784
The key bit of this error message is, "called during build". You need to wait until after build. This can be accomplished with either:
WidgetsBinding.instance?.addPostFrameCallback()
https://api.flutter.dev/flutter/widgets/WidgetsBinding-mixin.html
or
SchedulerBinding.instance?.addPostFrameCallback()
https://api.flutter.dev/flutter/scheduler/SchedulerBinding-mixin.html
Upvotes: 3
Reputation: 5718
You can workaround the error you are getting by scheduling the setState to be executed in the next frame and not potentially during build.
else if (snapid.data == "Error_ID") {
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
///This schedules the callback to be executed in the next frame
/// thus avoiding calling setState during build
setState(() {
have_ID = true;
resultName = "رقم تعريف اللاعب خاطئ";
});
});
...
Upvotes: 4
Reputation: 1242
You could use the property connectionState of snapid. This should generally work as connectionState is set to ConnectionState.done whenever the future is terminated.
FutureBuilder<String>(
future: fetchIdPlayer(idPlayer, bouquet),
builder: (context, snapid) {
if (snapshot.connectionState == ConnectionState.done) {
setState((){
//...
})
}
if (!snapid.hasData)
//...
else if (snapid.data == "Error_ID") {
//...
}
})
Upvotes: 3
Reputation: 520
You can just wrap the widget who will use resultName and have_ID with FutureBuilder .So there is no need to setState.you can also handle error as well .If you want to setState then use a asyn function and after result is fetched you can just call setState
Upvotes: 3