首页 > 解决方案 > Flutter SliverGrid 翻转轴

问题描述

tl; dr如何在 SliverGrid 中翻转 X 轴?

我必须在网格中显示一个长列表(每个单元格的大小都相同)。我可以从带有分页的服务器请求列表。现在用户应该能够在网格中的任何索引处打开该列表,并且应该能够双向滚动。

我正在尝试在 Flutter 中创建一个 GridView 并在滚动时在两个方向(向上和向下)延迟加载其内容。

我的第一次尝试是将infinite_listview修改为gridview。一开始看起来很有希望,但问题是min 和 maxExtend 被覆盖为始终为负数和正数无穷大。我将其改回正常行为,但只有 maxExtend 工作,minExtend 始终为 0,因此我无法向上滚动网格。

我的第二次尝试是带有视口的 Scrollable。在视口内,我放置了两个 SliverGrid(一个向上生长,一个向下生长)。我将视口的中心键设置为第二个 SliverGrid 的键,因此向下增长的 SliverGrid 的第一个项目是屏幕上的第一个单元格,用户可以滚动查看上方和下方的单元格。通过这种方法,滚动扩展保持不变。所以用户只能滚动直到他到达列表的末尾(或已经收到的单元格的末尾)。

class BidirectionalGridView extends StatelessWidget {
  static const Key centerKey = ValueKey("CENTER");
  final SliverGridDelegate gridDelegate;
  final IndexedWidgetBuilder itemBuilder;
  final int upperItemCount;
  final int lowerItemCount;

  const BidirectionalGridView({
    required this.gridDelegate,
    required this.itemBuilder,
    required this.upperItemCount,
    required this.lowerItemCount,
    Key? key,
  }) : super(key: key);

  int get totalItemCount => upperItemCount + lowerItemCount;

  @override
  Widget build(BuildContext context) {
    return Scrollable(
      axisDirection: AxisDirection.down,
      viewportBuilder: (context, position) {
        return Builder(builder: (context) {
          return Viewport(
              center: centerKey,
              offset: position,
              anchor: 0,
              slivers: [
                SliverGrid(
                  gridDelegate: gridDelegate,
                  delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                    return itemBuilder(context, upperItemCount - 1 - index);
                  }, childCount: upperItemCount),
                ),
                SliverGrid(
                  key: centerKey,
                  gridDelegate: gridDelegate,
                  delegate: SliverChildBuilderDelegate(
                      (BuildContext context, int index) {
                    return itemBuilder(context, upperItemCount + index);
                  }, childCount: lowerItemCount),
                ),
              ]);
        });
      },
    );
  }
}

使用此设置,我面临的问题是我必须翻转上 SliverGrid 的 X 轴才能获得右上半部分的顺序。但是我不知道怎么做。

上半部分 x 轴上的构建顺序错误

如何翻转 SliverGrid 中的 X 轴?还是在上半部实现从左到右的递增顺序?(我不知道每行的单元格数,据我所知,我无法通过翻译构建器中的索引来操纵顺序)。

演示代码:

var items = <int>[];
  for (var i = 0; i < 300; ++i) {
    items.add(i - 100);
  }

/*
 * ...
 */

BidirectionalGridView(
  upperItemCount: 100,
  lowerItemCount: 200,
  itemBuilder: (context, index) {
    return SizedBox.square(
      dimension: 72,
      child: Align(
        alignment: Alignment.center,
        child: Text("${items[index]}"),
      ),
    );
  },
  gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
    maxCrossAxisExtent: 72,
    crossAxisSpacing: 4,
    mainAxisSpacing: 4,
    childAspectRatio: 1,
  ),
),

标签: flutterdartmobileflutter-sliver

解决方案


由于我使用 aSliverGridDelegateWithMaxCrossAxisExtent作为我的网格委托,我只是将其子类化并稍微修改了getLayout方法。

class CrossAxisFlippedSliverGridDelegateWithMaxCrossAxisExtent
    extends SliverGridDelegateWithMaxCrossAxisExtent {
  /// Creates a delegate that makes grid layouts with tiles that have a maximum
  /// cross-axis extent and are flipped along the cross axis.
  /// ...
  const CrossAxisFlippedSliverGridDelegateWithMaxCrossAxisExtent({
    ...
    }) : super(...);

  @override
  SliverGridLayout getLayout(SliverConstraints constraints) {
    var tileLayout = super.getLayout(constraints) as SliverGridRegularTileLayout;

    return SliverGridRegularTileLayout(
      crossAxisCount: tileLayout.crossAxisCount,
      mainAxisStride: tileLayout.mainAxisStride,
      crossAxisStride: tileLayout.crossAxisStride,
      childMainAxisExtent: tileLayout.childMainAxisExtent,
      childCrossAxisExtent: tileLayout.childCrossAxisExtent,

      // Where the actual magic happens
      reverseCrossAxis: !tileLayout.reverseCrossAxis,
    );
  }
}

推荐阅读