Consumer returns empty List instead of actual data

Issue

I am using the provider package for flutter.
https://pub.dev/packages/provider

On Appstart, I use the following provider:

late Future<List<Art>> art;
late Future<List<Music>> music;
late Future<List<Furniture>> furniture;
late Future<List<Clothing>> clothing;
late Future<List<Flower>> flowers;
late Future<List<Interieur>> interieur;
late Future<List<Collectible>> allCollectibles;

  @override
  void initState() {
    super.initState();
    art = getAllArts();
    music = getAllMusic();
    furniture = getAllFurniture();
    clothing = getAllClothing();
    flowers = getAllFlowers();
    interieur = getAllInterieur();

    allCollectibles = () async {
      return [
        ...await art,
        ...await music,
        ...await furniture,
        ...await clothing,
        ...await flowers,
        ...await interieur,
      ];
    }();
  }
  @override
  Widget build(BuildContext context) {
    timeDilation = 1;

    return FutureBuilder(
      future: settings,
      builder: (context, snapshot) {
        if (snapshot.connectionState != ConnectionState.done) {
          return Center(child: CircularProgressIndicator());
        }

        return FutureProvider<List<Collectible>>(
                    create: (context) => allCollectibles, initialData: [],
                  ),});

later on, I use this consumer to retreive it:

@override
  Widget build(BuildContext context) {
    return Consumer<List<Collectible>>(builder: (context, collectibles, child) {
      return sendCollectibleDataFull(collectibles),
    });
  }

The list in the Method sendCollectibleDataFull is sometimes there and sometimes not.
If it is empty on call, it will stay empty.

I have updated to nullsafety hence the initialData: [], got mandatory. Before that, I always got a list from this.

Can I tell my Consumer/Provider to await the data before I retreive it?

Solution

Hi @lellek and welcome.

Okay there are a couple of things I want to point out.

You’re using the default FutureProvider constructor with a create callback but in fact you’re providing a value that already exists.

        return FutureProvider<List<Collectible>>(
                    create: (context) => allCollectibles, initialData: [],
                  ),});

If you already have a value and you’re not creating it with the Provider, you should use the .value constructor.

However, you could use the default constructor with the create callback and do all the work there instead of having this StatefulWidget doing some of the work (unless you just need these instances here as well).

This is not tested but it should look something like this:

FutureProvider<List<Collectible>>(
  create: (context) async {
    /// get collectibles in parallel
    final List<List<Collectible>> results = await Future.wait([
      getAllArts(), 
      getAllMusic(), 
      getAllFurniture(), 
      getAllClothing(), 
      getAllFlowers(), 
      getAllInterieur(),
    ]);

    /// flatten the results
    final allCollectables = [for (final collectibles in results) ...collectibles];

    return allCollectables;
  }, 
  initialData: [],
)

Can I tell my Consumer/Provider to await the data before I retreive it?

No, the point is that the initial value, in this case [], will be available from the provider and the tree will update when the future is resolved.

Answered By – Nico Spencer

Answer Checked By – Terry (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *