簡體   English   中英

將顫動網格視圖中的最后一行項目居中

[英]Centering the last row of items in a flutter gridview

我有一個動態的項目列表,我將輸出到一個 mainAxisCount 為 2(2 列網格)的GridView.count構造函數中。 如果列表長度是奇數,最后一行將只包含一個項目。 我希望這個單個項目在屏幕上居中,而不是與第一列對齊。 這能做到嗎?

你試試下面的東西怎么樣?

  1. 使用 2n 項生成 GridView。
  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最后一行的itemBuilder

您可以使用 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),
)

考慮到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,
        ),
)

免責聲明

這個解決方案沒有經過詳盡的測試,我把它貼在這里只是為了讓人們知道如何制作它。 但是,我在生產代碼中使用它。 如果我最終發現它有任何問題,我會在這里編輯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM