DEV Community

loading...

How to rebuild a ListView on Flutter using Provider async?

David MM🐍
Software developer (Django/Vue.js). Sometimes learning, sometimes teaching what I learn at https://letslearnabout.net/
・2 min read

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';

  @override
  _GenreListScreenState createState() => _GenreListScreenState();
}

class _GenreListScreenState extends State<GenreListScreen> {
  @override
  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,
                      );
                    }
                  },
                ),
              );
            },
          );
        },
      ),
    );
  }
}

Enter fullscreen mode Exit fullscreen mode

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;
  }
}

Enter fullscreen mode Exit fullscreen mode

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?

Discussion (1)

Collapse
nikolayshtanko profile image
nikolayshtanko • Edited

Hi!, David! I'm sorry for being late :)

You should add wrapper with class "ChangeNotifierProvider" in your widget's tree.
According to this example: flutter.dev/docs/development/data-...

Your should get something like this, I think (in attach image)
dev-to-uploads.s3.amazonaws.com/up...