【问题标题】:How to implement in Flutter a staggered grid view?如何在 Flutter 中实现交错网格视图?
【发布时间】:2018-10-30 07:41:30
【问题描述】:

我想在 Flutter 中实现交错网格视图 - 例如 Pinterest 交错网格视图(以前通过 their own Android Widget 实现,现在通过 Google 的 StaggeredGridLayoutManager 实现)。

所以要求是:

  • 项目是通过 API 获取的 - 所以我需要类似于 GridView.builder 或 ListView.builder 的东西,这样我就可以实现无限滚动
  • 要在网格视图中显示的每个项目由以下各项组成:
    • 图像 - 例如一张图片
    • 一些文本/视觉信息,包括:可变长度字符串(可能跨越两行或多行)、图标、一些其他文本(例如金额)

我知道有一个名为 flutter_staggered_grid_view 的插件,但这没有用,因为它需要提前知道网格的每个图块的精确高度 - 当然不是我的情况。

【问题讨论】:

    标签: flutter


    【解决方案1】:
    import 'package:flutter/material.dart';
    import 'package:flutter_staggered_grid_view.dart';
    
    class HomePage extends StatelessWidget {
    final List<String> images = [
    "https://uae.microless.com/cdn/no_image.jpg",
    "https://images-na.ssl-images-amazon.com/images/I/81aF3Ob-2KL._UX679_.jpg",
    "https://www.boostmobile.com/content/dam/boostmobile/en/products/phones/apple/iphone-7/silver/device-front.png.transform/pdpCarousel/image.jpg",
    "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSgUgs8_kmuhScsx-J01d8fA1mhlCR5-1jyvMYxqCB8h3LCqcgl9Q",
    "https://ae01.alicdn.com/kf/HTB11tA5aiAKL1JjSZFoq6ygCFXaw/Unlocked-Samsung-GALAXY-S2-I9100-Mobile-Phone-Android-Wi-Fi-GPS-8-0MP-camera-Core-4.jpg_640x640.jpg",
    "https://media.ed.edmunds-media.com/gmc/sierra-3500hd/2018/td/2018_gmc_sierra-3500hd_f34_td_411183_1600.jpg",
    "https://hips.hearstapps.com/amv-prod-cad-assets.s3.amazonaws.com/images/16q1/665019/2016-chevrolet-silverado-2500hd-high-country-diesel-test-review-car-and-driver-photo-665520-s-original.jpg",
    "https://www.galeanasvandykedodge.net/assets/stock/ColorMatched_01/White/640/cc_2018DOV170002_01_640/cc_2018DOV170002_01_640_PSC.jpg",
    "https://media.onthemarket.com/properties/6191869/797156548/composite.jpg",
    "https://media.onthemarket.com/properties/6191840/797152761/composite.jpg",
    ];
    
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          elevation: 0,
          backgroundColor: Colors.transparent,
          leading: Icon(
            Icons.arrow_back_ios,
            color: Colors.black,
          ),
          actions: [
            IconButton(
              onPressed: () {},
              icon: Icon(
                Icons.shopping_cart,
                color: Colors.black,
              ),
            )
          ],
        ),
        body: Column(
          children: [
            Padding(
              padding: const EdgeInsets.all(16.0),
              child: Row(
                children: [
                  Expanded(
                    child: Text(
                      "ShopX",
                      style: TextStyle(
                        fontFamily: 'avenir',
                        fontSize: 32,
                        fontWeight: FontWeight.w900,
                      ),
                    ),
                  ),
                  IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.view_list_rounded),
                  ),
                  IconButton(
                    onPressed: () {},
                    icon: Icon(Icons.grid_view),
                  ),
                ],
              ),
            ),
            Expanded(
              child: StaggeredGridView.countBuilder(
                crossAxisCount: 2,
                itemCount: images.length,
                mainAxisSpacing: 4.0,
                crossAxisSpacing: 4.0,
                itemBuilder: (context, index) {
                  return Container(
                    child: Card(
                      child: Column(
                        children: [
                          Image.network(images[index]),
                          Text("Some Text"),
                        ],
                      ),
                    ),
                  );
                },
                staggeredTileBuilder: (index) => StaggeredTile.fit(1),
              ),
            ),
          ],
        ),
      );
    }
    

    Try Your Result

    【讨论】:

      【解决方案2】:

      正如 Romain Rastler 所说,StaggeredGridView 是一个很好的解决方案,他会定期更新它,下面是一个示例,当我们从 Internet 获取内容时,我已经构建了不同高度的网格视图(您可以申请 API) :

      首先添加这个依赖(目前它的版本是0.3.0

      pubspec.yamlflutter_staggered_grid_view: 0.3.0

      然后导入这个:import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';

      import 'package:flutter/material.dart';
      
      import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
      
      class StaggeredGridExample extends StatefulWidget {
        @override
        _StaggeredGridExampleState createState() => _StaggeredGridExampleState();
      }
      
      class _StaggeredGridExampleState extends State<StaggeredGridExample> {
        final List<String> images = [
          "https://uae.microless.com/cdn/no_image.jpg",
          "https://images-na.ssl-images-amazon.com/images/I/81aF3Ob-2KL._UX679_.jpg",
          "https://www.boostmobile.com/content/dam/boostmobile/en/products/phones/apple/iphone-7/silver/device-front.png.transform/pdpCarousel/image.jpg",
          "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcSgUgs8_kmuhScsx-J01d8fA1mhlCR5-1jyvMYxqCB8h3LCqcgl9Q",
          "https://ae01.alicdn.com/kf/HTB11tA5aiAKL1JjSZFoq6ygCFXaw/Unlocked-Samsung-GALAXY-S2-I9100-Mobile-Phone-Android-Wi-Fi-GPS-8-0MP-camera-Core-4.jpg_640x640.jpg",
          "https://media.ed.edmunds-media.com/gmc/sierra-3500hd/2018/td/2018_gmc_sierra-3500hd_f34_td_411183_1600.jpg",
          "https://hips.hearstapps.com/amv-prod-cad-assets.s3.amazonaws.com/images/16q1/665019/2016-chevrolet-silverado-2500hd-high-country-diesel-test-review-car-and-driver-photo-665520-s-original.jpg",
          "https://www.galeanasvandykedodge.net/assets/stock/ColorMatched_01/White/640/cc_2018DOV170002_01_640/cc_2018DOV170002_01_640_PSC.jpg",
          "https://media.onthemarket.com/properties/6191869/797156548/composite.jpg",
          "https://media.onthemarket.com/properties/6191840/797152761/composite.jpg",
        ];
      
        @override
        Widget build(BuildContext context) {
          return Scaffold(
            body: StaggeredGridView.countBuilder(
              crossAxisCount: 4,
              itemCount: images.length,
              itemBuilder: (BuildContext context, int index) => Card(
                child: Column(
                  children: <Widget>[
                    Image.network(images[index]),
                    Text("Some text"),
                  ],
                ),
              ),
              staggeredTileBuilder: (int index) =>
              new StaggeredTile.fit(2),
              mainAxisSpacing: 4.0,
              crossAxisSpacing: 4.0,
            ),
          );
        }
      }
      

      【讨论】:

      • 你知道是否有可能让它动态化,这取决于图像吗?例如。如果它们处于纵向模式,则连续放置 4 张图像,但如果它们是横向模式,也可以将 2 张放在一行中?我发现的所有东西都是手动定义每个图像,但我找不到它的“动态”解决方案。
      • @Thomas 要确定设备方向是否为纵向,您可以使用OrientationBuilder。在这里阅读:stackoverflow.com/questions/50815014/…
      • 我指的是图像,而不是设备方向。
      • @Thomas 我没明白
      • 当我使用fit(2) 时,视图总是将两张图片并排放置。但我真正想要的是这样的:this,但在所有示例中,Tiles 的大小都是预定义的。我希望它们根据图像的纵横比尽可能大。
      【解决方案3】:

      试试这个。

      return new StreamBuilder<QuerySnapshot>(
            stream: Firestore.instance.collection(PRODUCTS_COLLECTION).snapshots(),
            builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
              if (!snapshot.hasData) return new Center(child: new CircularProgressIndicator());
              return new StaggeredGridView.count(
                physics: new BouncingScrollPhysics(),
                crossAxisCount: 2,
                children: buildGrid(snapshot.data.documents), 
                staggeredTiles: generateRandomTiles(snapshot.data.documents.length),
              );
            },
          );
      
      List<Widget> buildGrid(List<DocumentSnapshot> documents) {
        List<Widget> _gridItems = [];
        _products.clear();
      
        for (DocumentSnapshot document in documents) {
          _products.add(Product.fromDocument(document));
        }
      
        for (Product product in _products) {
          _gridItems.add(buildGridItem(product));
        }
      
        return _gridItems;
      }
      
      Widget buildGridItem(Product product) {
        return new GestureDetector(
          child: new Card(
            elevation: 2.0,
            margin: const EdgeInsets.all(5.0),
            child: new Stack(
              alignment: Alignment.center,
              children: <Widget>[
                new Hero(
                  tag: product.name,
                  child: new Image.network(product.imageUrl, fit: BoxFit.cover),
                ),
                new Align(
                  child: new Container(
                    padding: const EdgeInsets.all(6.0),
                    child: new Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: <Widget>[
                        new Text('\u20B9 ${product.price}',
                            style: new TextStyle(
                                color: Colors.white,
                                fontWeight: FontWeight.bold,
                                fontSize: 16.0)),
                        new Text(product.name,
                            style: new TextStyle(color: Colors.white)),
                      ],
                    ),
                    color: Colors.black.withOpacity(0.4),
                    width: double.infinity,
                  ),
                  alignment: Alignment.bottomCenter,
                ),
              ],
            ),
          ),
          onTap: () async {
            // TODO
          },
        );
      }
      
      List<StaggeredTile> generateRandomTiles(int count) {
        Random rnd = new Random();
        List<StaggeredTile> _staggeredTiles = [];
        for (int i=0; i<count; i++) {
          num mainAxisCellCount = 0;
          double temp = rnd.nextDouble();
      
          if (temp > 0.6) {
            mainAxisCellCount = temp + 0.5;
          } else if (temp < 0.3) {
            mainAxisCellCount = temp + 0.9;
          } else {
            mainAxisCellCount = temp + 0.7;
          }
          _staggeredTiles.add(new StaggeredTile.count(rnd.nextInt(1) + 1, mainAxisCellCount));
        }
        return _staggeredTiles;
      }
      

      【讨论】:

        【解决方案4】:

        我已经更新了flutter_staggered_grid_view 包。

        现在您可以像这样添加适合其内容大小的图块:

        您必须使用 StaggeredTile.fit(this.crossAxisCellCount) 构造函数来创建图块。

        希望对你有帮助。

        【讨论】:

        • 非常感谢!这行代码真的让我免去了很多麻烦。现在我的照片看起来很棒,上/下/右/左部分没有任何损失。
        • 酷!这也适用于无限列表吗?还是内容过多时性能会变差?
        【解决方案5】:

        试试这个Flutter Staggered Grid View 0.1.4

        希望对你有帮助。

        【讨论】:

        • 抱歉,这并不能回答问题,因为这个组件正是问题中提到的flutter_staggered_grid_view - 这是没有用的。
        • 什么都看不懂..写你想解释的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-09-20
        • 2021-07-09
        • 2021-07-12
        • 2022-07-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多