22
How to rebuild a ListView on Flutter using Provider async?
TL;DR: My Provider class doesn't trigger a rebuild, but I don't know what I'm doing wrong. Code provided below.
Hi,
I have a ListView.builder nested inside a Consumer.
While it creates as many MyProvider instances I have, if I delete or update an element, it does delete/update that element on the database, but it doesn't trigger a rebuild on the ListView builder, despite using notifyListeners().
This is (a simplified version of) my code:
genre_list.dart
class GenreListScreen extends StatefulWidget {
static const routeName = '/genre-list';
_GenreListScreenState createState() => _GenreListScreenState();
}
class _GenreListScreenState extends State<GenreListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: ...
),
body: Consumer<GenreProvider>(
builder: (context, data, index) {
var genres = data.getGenres;
return ListView.builder(
itemCount: genres.length,
itemBuilder: (context, index) {
var genre = genres[index];
return ListTile(
title: Text(genre['title']),
subtitle: IconButton(
icon: Icon(Icons.edit),
onPressed: () async {
try {
GenreProvider().updateGenre(genre['id'], 'Testing');
} on Exception catch (_) {
showSnackBar(
text: 'Unknown error updating the genre.',
color: Colors.red,
context: context,
);
}
},
),
);
},
);
},
),
);
}
}
genre_provider.dart
class GenreProvider with ChangeNotifier {
static List<Map<String, dynamic>> _genres = [];
Future fetchGenres() async {
try {
QuerySnapshot<Map<String, dynamic>> genres =
await FirebaseFirestore.instance.collection('genres').get();
_genres = [];
genres.docs.forEach((genre) {
_genres.add({'id': genre.id, 'title': genre['title']});
});
notifyListeners();
} catch (e) {
print(e);
}
}
Future updateGenre(String id, String title) async {
try {
await FirebaseFirestore.instance
.collection('genres')
.doc(id)
.update({'title': title});
var updatedGenre = _genres.firstWhere((element) => element['id'] == id);
updatedGenre['title'] = title;
notifyListeners();
} catch (e) {
throw (e);
}
}
List<Map<String, dynamic>> get getGenres {
return _genres;
}
}
After a successful login, I call fetchGenres to get the data. Then, I move to GenteListScreen and I can see all the genres I have on my Database.
Clicking on the 'Edit' button, I edit the Genre on the Database (normally a Dialog with a TextFormField, but for the example it is just a fixed string), but it doesn't trigger an UI rebuild on the ListView.
Calling setState(() {}) after the function, triggers a rebuild, and everything is fine, but this call should be done automatically by the Provider package via Consumer.
So the question is: What I am missing on the code to trigger the automatically rebuild?
22