So, i am working on weather application. I am getting list of cities from device memory:
late List<String> citiesList;
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: citiesList.length,
itemBuilder: (context, index) => CityCard(
name: citiesList[index],
dismissCallback: _deleteCity,
key: ValueKey(citiesList[index])
)
);
}
Future<void> _deleteCity(String name) async {
setState((){
citiesList.remove(name);
});
await CitiesListManager().deleteCity(name: name);
}
App renders list of CityCards, which are dismissibles. Every CityCard gets temperature of its own city from api on initialization. Until it gets response from Api, it displays spinner.
class _CityCardState extends State {
late Key key;
late String cityName;
late String temperature;
late Future<double> temperatureFuture;
late Function dismissCallback;
_CityCardState({
required String cityName,
required Function dismissCallback,
required Key key
}){
this.cityName = cityName;
this.dismissCallback = dismissCallback;
this.key = key;
}
@override
void initState() {
this.temperatureFuture = _getCurrentTemperature();
super.initState();
}
Widget build(BuildContext context){
return FutureBuilder(
future: temperatureFuture,
builder: (context, AsyncSnapshot<dynamic> snapshot){
if (snapshot.hasData) {
return Dismissible(
key: key,
onDismissed: (dismissDirection) async {
await dismissCallback(cityName);
},
confirmDismiss: (dismissDirection) => _showConfirmDeletionDialog(context),
child: Text(
'$cityName',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: FontConstants.MIDDLE_SIZE
),
),
);
}
else {
return Card();
}
}
);
}
_showConfirmDeletionDialog(BuildContext dismissableContext){
return showDialog<bool>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: const Text('Confirm city deletion'),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, true),
child: const Text('Delete'),
),
TextButton(
onPressed: () => Navigator.pop(context, false),
child: const Text('Cancel'),
),
],
),
).then((value) {
return value;
});
}
Future<double> _getCurrentTemperature() async {
final double currentTemperature = await
WeatherApi().getCurrentTemperature(cityName);
setState((){
temperature = currentTemperature.toString();
});
return currentTemperature;
}
}
Whenever i dismiss one of cities, every CityCard displays spinner (which i suppose witnesses about reinitializing). Same works for when i add a city. Looks like whenever List's length change, every item reinitializes and requesting api again.
So, im wondering if there's a way to avoid this reinitializing.
Every item reinitializing because you are calling getCurrentTemperature in the state of the CityCard.
The right logic should be to create a model like:
City {
String name;
double _temperature;
Future<double> _getCurrentTemperature() async {
...
}
}
Then populate a List of cities (better, if you need a key, think about using a Map) and pass every item to the widget CityCard, that probably could be stateless.
This way you assert that the list of cities is valorized once, with no rebuild of the widget CityCard.