简体   繁体   中英

Flutter: generate a List of Widgets with data from Firestore

I want my screen to display a limited number of products in GridView . All products are stored in the Firebase Firestore and their images are in the Firebase storage. In order to do that I use GridView.count , which one of its parameters is List<Widget> children . I wanted to generate a List using List.generate and each time fetch a product from the Firestore and its image from the storage to create a widget that represents a product. The problem is that fetching the products from Firebase is asynchronous, and the generator function of List.generate cannot be async . Moreover I'm returning Future<List<Widget>> instead of List<Widget> to the children param of GridView.count Any ideas of how to overcome this?

here is my code:


GridView.count(
  primary: false,
  crossAxisCount: 2,
  padding: const EdgeInsets.all(20),
  crossAxisSpacing: 10,
  mainAxisSpacing: 10,
  //FIXME: The argument type 'Future<List<Widget>>' can't be assigned to the parameter type 'List<Widget>':
  children: _buildGridTileList(15),
)

Future<List<Widget>> _buildGridTileList(int count) async {
    var counterCollection = await FirebaseFirestore.instance.collection("Products").doc("Counter").get();
    var counterData = counterCollection.data();
    return List.generate(
      count,
      (index) async {
        //FIXME: Doesn't work beacuse generator cannot be async!!
        var avatarUrl = await FirebaseStorage.instance.ref('${counterData[index]}').child('${counterData[index]}').getDownloadURL();
        return Container(
          child: Center(
            child: NetworkImage(avatarUrl),
          ),
        );
      }
    );
  }

You can't have async code in your build function because it's the function assembling the widget tree to render immediately to the screen, instead what you do is use a StreamBuilder widget which gives you access to async events, the stream is listening to future data arriving from Firebase and rebuilds that section of the widget tree when the asynchronous event arrives. here is how it's done, this code should be close to what you need:

return StreamBuilder(
  stream: FirebaseFirestore.instance.collection("Products").snapshots(),
  builder:
  (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
  if (!snapshot.hasData) {
    return Container();
  } else {
    return GridView.count(
      primary: false,
      crossAxisCount: 2,
      padding: const EdgeInsets.all(20),
      crossAxisSpacing: 10,
      mainAxisSpacing: 10,
      children: List.generate(snapshot.data.docs.length, (index) {
        return Image.network(snapshot.data.docs[index].imageUrl);
        }),
      );
    }
  });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM