Reputation: 59
Hello and thanks for reading this, i was trying to call a setState
inside a onTap()
function that is inside a FutureBuilder
i was reading other threads with similar problems, but i can't find a fix, there say that problem is the setState
dont works fine with async.
i call setState
cause i have a ExpansionTile
with ListTiles
with remote data, see the code, i try to update the data gattering when i tap another option, but when i call setState
the data gattering is maded but nothing happens in UI.
I tried to put the setState call in the whenComplete
of the async onTap, but still no working, i readed that if i put a symbolic return and after the setState
but still no working, i tried to put in different points of the async, i tried to delete the showSnackBar
and see if it works with only the new data gattering function and setState, but dont works.
i hope you can uderstand me, here is a little piece of my code, if you need more, let me know:
ExpansionTile(
collapsedTextColor: Colors.black87,
textColor: Colors.white,
collapsedBackgroundColor: Colors.white,
backgroundColor: Colors.blue,
title: Text(element['result'] == ''
? AppLocalizations.of(context)!.noTileSelected
: element['result']),
children: List<Widget>.generate(
6,
(index) => index == 0
? ListTile(
onTap: () async {
bool res = await analisisModel
.updateField(
'',
element['id'].toString(),
bach,
id,
)
.whenComplete(() {
return;
});
if (res) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(
AppLocalizations.of(
context)!
.savedSnack),
duration:
Duration(milliseconds: 500),
));
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(
AppLocalizations.of(
context)!
.errorSnack),
duration: Duration(
milliseconds: 1500),
));
}
setState(() {
this._future =
analisisModel.getFinalData(
idCue, bach, id);
});
},
title: Text(
'---',
style:
TextStyle(color: Colors.white),
),
)
: ListTile(
title: Text(
element['opci$index'] == ''
? AppLocalizations.of(
context)!
.noRegistersText
: element['opci$index'],
style: TextStyle(
color: Colors.white)),
onTap: () async {
bool res = await analisisModel
.updateField(
element['opci$index'],
element['id'].toString(),
bach,
id,
)
.whenComplete(() {
return;
});
if (res) {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(
AppLocalizations.of(
context)!
.savedSnack),
duration:
Duration(milliseconds: 500),
));
} else {
ScaffoldMessenger.of(context)
.showSnackBar(SnackBar(
content: Text(
AppLocalizations.of(
context)!
.errorSnack),
duration: Duration(
milliseconds: 1500),
));
}
setState(() {
this._future =
analisisModel.getFinalData(
idCue, bach, id);
});
},
))))
this is the piece of code that doesn't work, i've deleted showSnacBar
for easier reading:
ListTile(
onTap: () async {
bool res = await analisisModel.updateField(
'',
element['id'].toString(),
bach,
id,
).whenComplete(() { return;});
//that set state don't make anything in UI
setState(() {
this._future = analisisModel.getFinalData(
idCue, bach, id);
});
},
title: Text('---',
style: TextStyle(color: Colors.white),
),
)
Upvotes: 0
Views: 440
Reputation: 63559
while using FutureBuilder
we promised to wait only once. and it will rebuild
only once, if you are trying to modify value like using setState
inside FutureBuilder
or even outside of that widget, it will rebuild everything. better you can use stateManagement
for this situation.
here is an example how FutureBuilder
rebuild everytime.
class CalculatableTextFormField extends StatefulWidget {
@override
_CalculatableTextFormFieldState createState() =>
_CalculatableTextFormFieldState();
}
class _CalculatableTextFormFieldState extends State<CalculatableTextFormField> {
int data = 0;
int _numberOfFuture = 0;
Future<int> dummyFuture() async {
print("future called ${_numberOfFuture++}");
return Future.delayed(Duration(seconds: 1)).then((value) => 4);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
Text("inside FutureBuilder"),
FutureBuilder<int>(
future: dummyFuture(),
builder: (context, snapshot) {
if (snapshot.hasData &&
snapshot.connectionState == ConnectionState.done) {
return Column(
children: [
Builder(
builder: (context) {
return Text(
"Got Data from Future $data",
key: ValueKey("text"),
);
},
),
ElevatedButton(
onPressed: () async {
bool result = await Future.delayed(Duration(seconds: 2))
.then((value) => true);
if (result)
setState(() {
data++;
});
print("after click $data");
},
child: Text(
"Click",
),
),
],
);
} else
return CircularProgressIndicator();
},
),
ElevatedButton(
onPressed: () {
setState(() {});
},
child: Text("just setState")),
],
));
}
}
Upvotes: 1