Flutter - 不可滚动的网格

Posted

技术标签:

【中文标题】Flutter - 不可滚动的网格【英文标题】:Flutter - Non-scrollable Grid 【发布时间】:2020-02-03 12:10:09 【问题描述】:

有没有办法构建一个本身不可滚动且大小取决于其子级的网格,就像我们可以在行或列中指定 mainAxisSize: MainAxisSize.min 一样?

为您提供全局 -

我正在尝试创建一个取决于设备宽度的响应式布局。 它应该分成两部分,通过列无缝连接。

1) 2 个大小取决于屏幕宽度的大容器,考虑到它们之间的小空间。每个容器的宽度和高度都相同(方形容器)。

2) 相同的想法,但有 3 行,每行包含 3 个较小的容器。 这将创建一个网格。尽管网格本身不能滚动并且其大小将取决于其子级,但这一点非常重要。它只能与 SingleChildScrollView 中包含的页面的其余部分一起滚动。

特别是因为每个容器的高度都需要与其宽度相同,我正在考虑使用行、列和 LayoutBuilder 的组合 - 它们为我提供了我需要的所有功能。

但是,在手动执行操作之前,我想知道是否有一些开箱即用的方法。

【问题讨论】:

【参考方案1】:

这样的?

SingleChildScrollView(
  child: Column(
    children: <Widget>[
      Row(
        children: <Widget>[
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(10.0),
              child: AspectRatio(
                aspectRatio: 1.0,
                child: Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    border: Border.all(width: 3.0, color: Colors.green),
                  ),
                ),
              ),
            ),
          ),
          Expanded(
            child: Padding(
              padding: const EdgeInsets.all(10.0),
              child: AspectRatio(
                aspectRatio: 1.0,
                child: Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    border: Border.all(width: 3.0, color: Colors.green),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
      Container(
        padding: const EdgeInsets.all(10.0),
        child: GridView.builder(
          physics: NeverScrollableScrollPhysics(),
          shrinkWrap: true,
          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
            crossAxisCount: 3,
            childAspectRatio: 1.0,
            mainAxisSpacing: 10.0,
            crossAxisSpacing: 10.0,
          ),
          itemCount: 21,
          itemBuilder: (context, index) 
            return Container(
              decoration: BoxDecoration(
                border: Border.all(width: 3.0),
              ),
            );
          ,
        ),
      ),
    ],
  ),
)

【讨论】:

非常感谢你。我花了将近 10 个小时来做​​这个......再次感谢伙计。 @Emon 我花了 6 个小时试图解决这个问题,哈哈 "......失去了将近 10 个小时","......花了 6 个小时......" smh,我喜欢编程【参考方案2】:

table widget 可能是比 GridView(janstol 的答案)更简单的方法,因为它是专为您正在寻找的东西而构建的。尽管这不适用于您的情况,但如果您的小部件可以具有不同的大小,Table 将特别有用 - 它会相对于每列/行中最宽/最高的小部件(分别)垂直和水平对齐小部件。

这里是 janstol 的解决方案代码重写为使用表。请注意,Table 要求所有行的长度相同,因此您的前两个元素行需要位于表之外。

Widget bigBox = Expanded(
            child: Padding(
              padding: const EdgeInsets.all(10.0),
              child: AspectRatio(
                aspectRatio: 1.0,
                child: Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    border: Border.all(width: 3.0, color: Colors.green),
                  ),
                ),
              ),
            ),
          );
          Widget smallBox = Padding(
              padding: const EdgeInsets.all(10.0),
              child: AspectRatio(
                aspectRatio: 1.0,
                child: Container(
                  width: double.infinity,
                  decoration: BoxDecoration(
                    border: Border.all(width: 3.0),
                  ),
                ),
              ),
            );
          return Scaffold(
            body: SingleChildScrollView(
              child: Column(
                children: [
                  Row(
                    children: List.filled(2, bigBox),
                  ),
                  Container(
                    child: Table(
                      children: List.filled(
                          // 7 rows instead of 3 just to demonstrate the scrolling functionality
                          7,
                          TableRow(
                            children: List.filled(3, smallBox),
                          )
                      ),
                    ),
                  ),
                ],
              ),
            ),

【讨论】:

欢迎提供指向解决方案的链接,但请确保您的答案在没有它的情况下有用:在链接周围添加上下文,以便您的其他用户了解它是什么以及为什么存在它,然后引用在目标页面不可用的情况下,您链接到的页面的最相关部分。 感谢 Zahra 的反馈,我已经完全重写了我的答案,使其更加彻底,并且无需链接即可独立使用。【参考方案3】:

您可以为其创建一个自定义小部件,这是code,用于从google ui 包中创建一个不可滚动的网格小部件。

import 'package:flutter/material.dart';

/// Create a grid.
class GoogleGrid extends StatelessWidget 
  const GoogleGrid(
    Key? key,
    this.columnCount = 2,
    this.gap,
    this.padding,
    required this.children,
  ) : super(key: key);

  /// Number of column.
  final int columnCount;

  /// Gap to separate each cell.
  final double? gap;

  /// An empty space.
  final EdgeInsets? padding;

  /// The widgets below this widget in the tree.
  final List<Widget> children;

  @override
  Widget build(BuildContext context) 
    return Container(
      padding: padding,
      child: Column(children: _createRows()),
    );
  

  List<Widget> _createRows() 
    final List<Widget> rows = [];
    final childrenLength = children.length;
    final rowCount = (childrenLength / columnCount).ceil();

    for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) 
      final List<Widget> columns = _createRowCells(rowIndex);
      rows.add(Row(children: columns));
      if (rowIndex != rowCount - 1) rows.add(SizedBox(height: gap));
    

    return rows;
  

  List<Widget> _createRowCells(int rowIndex) 
    final List<Widget> columns = [];
    final childrenLength = children.length;

    for (int columnIndex = 0; columnIndex < columnCount; columnIndex++) 
      final cellIndex = rowIndex * columnCount + columnIndex;
      if (cellIndex <= childrenLength - 1) 
        columns.add(Expanded(child: children[cellIndex]));
       else 
        columns.add(Expanded(child: Container()));
      

      if (columnIndex != columnCount - 1) columns.add(SizedBox(width: gap));
    

    return columns;
  

使用方法与使用列或行小部件相同,您可以定义列数、间隙、填充和边距。

GoogleGrid(
  gap: 16,
  padding: const EdgeInsets.all(16),
  children: [
    Text("1"),
    Text("2"),
    Text("3"),
    Text("4"),
    Text("5"),
    Text("6"),
  ],
);

【讨论】:

以上是关于Flutter - 不可滚动的网格的主要内容,如果未能解决你的问题,请参考以下文章

交错网格视图在 Flutter 中不向上滚动

如何使用网格 Flutter 同时滚动小部件

Flutter:ListView不可滚动,不可弹跳

Flutter GridView 页脚(用于指示无限滚动的负载)

flutter 实现不可滚动的ListView构建器

Flutter - 在网格视图的末尾添加一个按钮