I'm trying to build a Save button that lets the user save/ unsave (like/ unlike) items displayed in a ListView
.
What I have so far:
Future<bool>
that determines which state the icon should be rendered inFutureBuilder
that calls the repository and renders the icon as either saved/ unsaved.Icon
wrapped in a GestureDetector
that makes a call to the repository within a setState
call when onTap
is invoked.`
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _repository.isSaved(item),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.none:
case ConnectionState.active:
return Icon(Icons.favorite_border);
case ConnectionState.done:
return GestureDetector(
child: Icon(
snapshot.data ? Icons.favorite : Icons.favorite_border,
color: snapshot.data ? Colors.red : null),
onTap: () {
setState(() {
if (snapshot.data) {
_repository.removeItem(item);
} else {
_repository.saveItem(item);
}
});
},
);
}
});
}
`
The issue I'm having is that when I tap to save an item in the list - the item is saved however the icon is not updated until I scroll it off screen then back on again. When I tap to unsave an item, it's state is reflected immediately and updates as expected.
I suspect that the save call is taking longer to complete than the delete call. Both of these are async
operations:
void removeItem(String item) async {
_databaseClient.deleteItem(item);
}
void saveItem(String item) async {
_databaseClient.saveItem(item);
}
@override
void deleteItem(String item) async {
var client = await db;
client.delete("items_table", where: "item = '$item'"); // returns Future<int> but I'm not using this currently
}
void _saveItem(String item) async {
var client = await db;
client.insert("items_table", item); // returns Future<int> but I'm not using this currently
}
Future<bool> isSaved(String name) async {
var matching = await _databaseClient.getNameByName(name);
return matching != null && matching.isNotEmpty;
}
Any idea what could be causing this?
When you tap the button, setState will be called. then FutureBuilder will wait for the isSaved method. if the save method is being in progress. isSaved will return the last state and Icon will not change.
I suggest to wait for the result of Save and Remove method and call setState after that.
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _repository.isSaved(item),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.none:
case ConnectionState.active:
return Icon(Icons.favorite_border);
case ConnectionState.done:
return GestureDetector(
child: Icon(
snapshot.data ? Icons.favorite : Icons.favorite_border,
color: snapshot.data ? Colors.red : null),
onTap: () async{
if (snapshot.data) {
await _repository.removeItem(item);
} else {
await _repository.saveItem(item);
}
setState(() {
});
},
);
}
});
}
However, if the methods take so long, it delays which cause bad user experience. it better to change the icon to progress circle during running methods.