如何在 Flutter 的同一屏幕中管理两个带有数据的 ListView

Posted

技术标签:

【中文标题】如何在 Flutter 的同一屏幕中管理两个带有数据的 ListView【英文标题】:How to manage two ListView with data in firestore in the same screen in Flutter 【发布时间】:2020-01-16 02:20:51 【问题描述】:

我正在构建一个 Flutter 应用程序,我有一个带有 2 个 ListView 的屏幕。数据源是一个 Firestore 数据库,两个列表都相同,但是一个列表仅显示图像,而另一个列表显示图像和其他信息。

我设法找到了一个显示两个列表的解决方案,但它似乎不是最有效的,因为我相信我正在下载相同的数据 2 次。你有什么建议可以让它变得更好吗???

请看下面的代码

final _firestore = Firestore.instance;

class ItemPage extends StatefulWidget 
  @override
  _ItemPageState createState() => _ItemPageState();


class _ItemPageState extends State<ItemPage> 
  bool showSpinner = false;

  @override
  Widget build(BuildContext context) 
    return CupertinoPageScaffold(
      navigationBar: CupertinoNavigationBar(
        heroTag: 'itemppage',
        transitionBetweenRoutes: false,
        middle: Text(
          appData.categoryName,
          style: kSendButtonTextStyle,
        ),
      ),
      child: Scaffold(
        backgroundColor: kColorPrimary,
        body: ModalProgressHUD(
          inAsyncCall: showSpinner,
          child: Column(
            children: <Widget>[
              Expanded(
                child: ItemList(),
              ),
              Container(
                height: 80.0,
                child: ItemListBottomScroll(),
              )
            ],
          ),
        ),
      ),
    );
  


class ItemList extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return StreamBuilder<QuerySnapshot>(
      stream: _firestore
          .collection('books')
          .snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
        if (snapshot.hasError) return new Text('Error: $snapshot.error');
        switch (snapshot.connectionState) 
          case ConnectionState.waiting:
            return new Text(
              'Loading...',
              style: kSendButtonTextStyle,
            );
          default:
            return new PageView(
              scrollDirection: Axis.horizontal,
              children:
                  snapshot.data.documents.map((DocumentSnapshot document) 
                return SingleChildScrollView(
                  child: Column(
                    children: <Widget>[
                      Container(
                        margin: EdgeInsets.all(10.0),
                        child: Stack(
                          children: <Widget>[
                            CachedNetworkImage(
                                imageUrl: document['url'],
                                placeholder: (context, url) =>
                                    new CircularProgressIndicator(),
                                errorWidget: (context, url, error) =>
                                    new Icon(Icons.error),
                                width: MediaQuery.of(context).size.width - 20,
                                height:
                                    (MediaQuery.of(context).size.width - 20),
                                fit: BoxFit.cover),
                            Positioned(
                              bottom: 0.0,
                              right: 0.0,
                              child: IconButton(
                                color: kColorAccent.withOpacity(0.8),
                                iconSize: 50.0,
                                icon: Icon(Icons.add_circle),
                                onPressed: () 
                                  print(document['name'] +
                                      document.documentID +
                                      ' clicked');
                                ,
                              ),
                            ),
                          ],
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.only(
                            left: 10.0, right: 10.0, bottom: 10.0),
                        child: Row(
                          children: <Widget>[
                            Expanded(
                              child: Text(
                                document['name'],
                                style: kSendButtonTextStyle,
                              ),
                              flex: 3,
                            ),
                            Expanded(
                              child: Text(
                                appData.currency + document['price'],
                                textAlign: TextAlign.right,
                                style: kDescriptionTextStyle,
                              ),
                              flex: 1,
                            ),
                          ],
                        ),
                      ),
                      Padding(
                        padding: const EdgeInsets.only(left: 10.0, right: 10.0),
                        child: Container(
                          child: Text(
                            document['description'],
                            style: kDescriptionTextStyle,
                          ),
                          width: double.infinity,
                        ),
                      )
                    ],
                  ),
                );
              ).toList(),
            );
        
      ,
    );
  


class ItemListBottomScroll extends StatelessWidget 
  @override
  Widget build(BuildContext context) 
    return StreamBuilder<QuerySnapshot>(
      stream: _firestore
          .collection('books')
          .snapshots(),
      builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) 
        if (snapshot.hasError) return new Text('Error: $snapshot.error');
        switch (snapshot.connectionState) 
          case ConnectionState.waiting:
            return new Text('Loading...');
          default:
            return new ListView(
              scrollDirection: Axis.horizontal,
              children:
                  snapshot.data.documents.map((DocumentSnapshot document) 
                return Stack(
                  children: <Widget>[
                    Container(
                      height: 80.0,
                      width: 90.0,
                      padding: EdgeInsets.only(left: 10.0),
                      child: GestureDetector(
                        onTap: () 
                          print(document['name'] +
                              document.documentID +
                              ' bottom clicked');
                        ,
                        child: new CachedNetworkImage(
                            imageUrl: document['url'],
                            placeholder: (context, url) =>
                                new CircularProgressIndicator(),
                            errorWidget: (context, url, error) =>
                                new Icon(Icons.error),
                            fit: BoxFit.cover),
                      ),
                    ),
                  ],
                );
              ).toList(),
            );
        
      ,
    );
  

【问题讨论】:

【参考方案1】:

为了从同一个Stream 读取这两个信息,您需要先将其公开为广播流:

final streamQuery = _firestore
          .collection('books')
          .snapshots()
          .asBroadcastStream();
return StreamBuilder<QuerySnapshot>(
      stream: streamQuery,
      builder: ...

【讨论】:

以上是关于如何在 Flutter 的同一屏幕中管理两个带有数据的 ListView的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - 如何让带有孩子的容器占据整个屏幕

如何使用 Flutter 更改状态栏颜色?

Flutter 中 PageController 的 Provider 状态管理

在 Flutter 中弹出屏幕后如何调用函数来更新值?

如何在颤动的两个不同屏幕上使用相同的块?

How To - 带有持久子屏幕的 Flutter Drawer