【问题标题】:Centering the last row of items in a flutter gridview在颤动的网格视图中将最后一行项目居中
【发布时间】:2020-06-18 21:16:11
【问题描述】:

我有一个项目的动态列表,我正在输出到一个 mainAxisCount 为 2(2 列网格)的 GridView.count 构造函数中。如果列表长度为奇数,则最后一行将只包含一个项目。我希望这一项在屏幕上居中,而不是与第一列对齐。这个可以吗?

【问题讨论】:

标签: flutter gridview flutter-layout


【解决方案1】:

你试试下面的东西怎么样?

  1. 使用 2n 项生成 GridView。
  2. 如果有剩余项,则为最后一项添加小部件。

【讨论】:

    【解决方案2】:

    您可以为此使用flutter_staggered_grid_view 包。

    例子:

     StaggeredGridView.countBuilder(
      crossAxisCount: 4,
      itemCount: 8,
      itemBuilder: (BuildContext context, int index) => Container(
          color: Colors.green,
          child: Center(
            child: new CircleAvatar(
              backgroundColor: Colors.white,
              child: new Text('$index'),
            ),
          )),
      staggeredTileBuilder: (int index) =>
          StaggeredTile.count(2, index.isEven ? 2 : 1),
      mainAxisSpacing: 4.0,
      crossAxisSpacing: 4.0,
    )
    
    1. staggeredTileBuilder 中返回最后一行的跨度计数为 1
    2. itemBuilder 中的最后一行返回居中小部件

    【讨论】:

      【解决方案3】:

      你可以使用 StaggeredGridView

      StaggeredGridView.countBuilder(
          crossAxisCount: 2,
          mainAxisSpacing: 4.0,
          crossAxisSpacing: 4.0,
          itemCount: totalCount,
          itemBuilder: (BuildContext context, int index) {
            return Center(
              child: Container(
                width: w,
                height: w,
                color: Colors.redAccent,
              ),
            );
          },
          staggeredTileBuilder: (int index) =>
              new StaggeredTile.count(index == totalCount-1 ? 2 : 1, 1),
      )
      

      【讨论】:

        【解决方案4】:

        考虑到flutter_staggered_grid_view,有两个答案。虽然它是一个不错的包,但我认为它比你想要的要多得多。

        我正在提供更手动的自定义实现。

        import 'dart:math';
        
        import 'package:flutter/rendering.dart';
        import 'package:flutter/widgets.dart';
        
        /// A [SliverGridLayout] that provide a way to customize the children geometry.
        class SliverGridWithCustomGeometryLayout extends SliverGridRegularTileLayout {
          /// The builder for each child geometry.
          final SliverGridGeometry Function(
            int index,
            SliverGridRegularTileLayout layout,
          ) geometryBuilder;
        
          SliverGridWithCustomGeometryLayout({
            @required this.geometryBuilder,
            @required int crossAxisCount,
            @required double mainAxisStride,
            @required double crossAxisStride,
            @required double childMainAxisExtent,
            @required double childCrossAxisExtent,
            @required bool reverseCrossAxis,
          })  : assert(geometryBuilder != null),
                assert(crossAxisCount != null && crossAxisCount > 0),
                assert(mainAxisStride != null && mainAxisStride >= 0),
                assert(crossAxisStride != null && crossAxisStride >= 0),
                assert(childMainAxisExtent != null && childMainAxisExtent >= 0),
                assert(childCrossAxisExtent != null && childCrossAxisExtent >= 0),
                assert(reverseCrossAxis != null),
                super(
                  crossAxisCount: crossAxisCount,
                  mainAxisStride: mainAxisStride,
                  crossAxisStride: crossAxisStride,
                  childMainAxisExtent: childMainAxisExtent,
                  childCrossAxisExtent: childCrossAxisExtent,
                  reverseCrossAxis: reverseCrossAxis,
                );
        
          @override
          SliverGridGeometry getGeometryForChildIndex(int index) {
            return geometryBuilder(index, this);
          }
        }
        
        /// Creates grid layouts with a fixed number of tiles in the cross axis, such
        /// that fhe last element, if the grid item count is odd, is centralized.
        class SliverGridDelegateWithFixedCrossAxisCountAndCentralizedLastElement
            extends SliverGridDelegateWithFixedCrossAxisCount {
          /// The total number of itens in the layout.
          final int itemCount;
        
          SliverGridDelegateWithFixedCrossAxisCountAndCentralizedLastElement({
            @required this.itemCount,
            @required int crossAxisCount,
            double mainAxisSpacing = 0.0,
            double crossAxisSpacing = 0.0,
            double childAspectRatio = 1.0,
          })  : assert(itemCount != null && itemCount > 0),
                assert(crossAxisCount != null && crossAxisCount > 0),
                assert(mainAxisSpacing != null && mainAxisSpacing >= 0),
                assert(crossAxisSpacing != null && crossAxisSpacing >= 0),
                assert(childAspectRatio != null && childAspectRatio > 0),
                super(
                  crossAxisCount: crossAxisCount,
                  mainAxisSpacing: mainAxisSpacing,
                  crossAxisSpacing: crossAxisSpacing,
                  childAspectRatio: childAspectRatio,
                );
        
          bool _debugAssertIsValid() {
            assert(crossAxisCount > 0);
            assert(mainAxisSpacing >= 0.0);
            assert(crossAxisSpacing >= 0.0);
            assert(childAspectRatio > 0.0);
            return true;
          }
        
          @override
          SliverGridLayout getLayout(SliverConstraints constraints) {
            assert(_debugAssertIsValid());
            final usableCrossAxisExtent = max(
              0.0,
              constraints.crossAxisExtent - crossAxisSpacing * (crossAxisCount - 1),
            );
            final childCrossAxisExtent = usableCrossAxisExtent / crossAxisCount;
            final childMainAxisExtent = childCrossAxisExtent / childAspectRatio;
            return SliverGridWithCustomGeometryLayout(
              geometryBuilder: (index, layout) {
                return SliverGridGeometry(
                  scrollOffset: (index ~/ crossAxisCount) * layout.mainAxisStride,
                  crossAxisOffset: itemCount.isOdd && index == itemCount - 1
                      ? layout.crossAxisStride / 2
                      : _getOffsetFromStartInCrossAxis(index, layout),
                  mainAxisExtent: childMainAxisExtent,
                  crossAxisExtent: childCrossAxisExtent,
                );
              },
              crossAxisCount: crossAxisCount,
              mainAxisStride: childMainAxisExtent + mainAxisSpacing,
              crossAxisStride: childCrossAxisExtent + crossAxisSpacing,
              childMainAxisExtent: childMainAxisExtent,
              childCrossAxisExtent: childCrossAxisExtent,
              reverseCrossAxis: axisDirectionIsReversed(constraints.crossAxisDirection),
            );
          }
        
          double _getOffsetFromStartInCrossAxis(
            int index,
            SliverGridRegularTileLayout layout,
          ) {
            final crossAxisStart = (index % crossAxisCount) * layout.crossAxisStride;
        
            if (layout.reverseCrossAxis) {
              return crossAxisCount * layout.crossAxisStride -
                  crossAxisStart -
                  layout.childCrossAxisExtent -
                  (layout.crossAxisStride - layout.childCrossAxisExtent);
            }
            return crossAxisStart;
          }
        }
        

        说明

        稍微解释一下,这段代码所做的是它是SliverGridLayout 的自定义实现,这是告诉Flutter 如何在网格布局上定位子级的类的类型。

        这里比较重要的方法是getGeometryForChildIndex。这是根据index 说明每个孩子应该定位的方法。但是请注意,我们通过一个参数将这个方法暴露给我将要讨论的下一个类。

        我们实现的下一个类是SliverGridDelegate。我们必须自定义实现才能使用SliverGridWithCustomGeometryLayout,因为没有其他方法可以让委托使用特定的SliverGridLayout。在这里,我们使用参数geometryBuilder 来委派它返回SliverGridGeometry 的角色。

        geometryBuilder 的这个实现是所有魔法发生的地方。它基本上是原始SliverGridRegularTileLayout 方法的副本,但有一个变化。我们检查元素的索引是否是偶数以及它是否也是最后一个。如果两个检查都通过,我们返回一个居中的位置。否则,我们将返回它本来应该有的位置。

        要使用我们的解决方案,只需将其传递给GridView 上的gridDelegate 参数即可。示例:

        GridView.builder(
            itemCount: 9,
            itemBuilder: (_, __) =>
                Container(width: 100, height: 100, color: Colors.red),
            gridDelegate:
                SliverGridDelegateWithFixedCrossAxisCountAndCentralizedLastElement(
                    itemCount: checklist.sections.length(),
                    crossAxisCount: 2,
                    childAspectRatio: 0.825,
                ),
        )
        

        免责声明

        这个解决方案没有经过详尽的测试,我在这里发布它只是为了让一个知道如何制作它的人。但是,我在生产代码中使用它。如果我最终发现它有任何问题,我会在这里编辑。

        【讨论】:

        • 你是否实现了特定索引中的列折叠? @Mateus Felipe我确实实现了flutter_staggered_grid_view,但是当容器调整大小时gridview没有重建列宽。 :((
        • 是的,这个例子很固定,但应该不难概括。
        猜你喜欢
        • 2013-10-11
        • 2018-03-11
        • 1970-01-01
        • 1970-01-01
        • 2019-01-16
        • 2022-01-17
        • 2018-04-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多