You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Flutter中实现与GeForce Experience同款固定项尺寸的响应式GridView.builder

Flutter实现类似GeForce Experience的响应式流式布局

Flutter原生GridView.builder无法直接实现「固定基础尺寸+剩余宽度自动填充」的布局效果,以下提供两种方案分别适配小数据量和大数据量场景:

方案1:基于Wrap的实现(适合数据量较小场景)

这种方式简单直观,通过LayoutBuilder获取容器可用宽度,动态计算项的宽度,让最后一行的项自动填充剩余空间:

class ResponsiveStretchWrap extends StatelessWidget {
  final double itemBaseWidth; // 项的基础固定宽度
  final List<Widget> items;

  const ResponsiveStretchWrap({
    super.key,
    required this.itemBaseWidth,
    required this.items,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final availableWidth = constraints.maxWidth;
        final crossAxisCount = (availableWidth / itemBaseWidth).floor();
        if (crossAxisCount == 0) return const SizedBox.shrink();

        // 拆分数据为多行
        List<List<Widget>> rows = [];
        for (int i = 0; i < items.length; i += crossAxisCount) {
          rows.add(items.sublist(i, i + crossAxisCount > items.length ? items.length : i + crossAxisCount));
        }

        return Column(
          children: rows.map((rowItems) {
            final isLastRow = rows.last == rowItems;
            final itemCount = rowItems.length;
            // 最后一行项数不足时,拉伸填充剩余宽度
            final itemWidth = isLastRow && itemCount < crossAxisCount
                ? availableWidth / itemCount
                : itemBaseWidth;

            return Row(
              children: rowItems.map((item) {
                return SizedBox(
                  width: itemWidth,
                  child: item,
                );
              }).toList(),
            );
          }).toList(),
        );
      },
    );
  }
}

使用示例:

ResponsiveStretchWrap(
  itemBaseWidth: 200,
  items: List.generate(10, (index) {
    return Container(
      margin: const EdgeInsets.all(4),
      color: Colors.blue[100 * (index % 9)],
      height: 150,
      child: Center(child: Text('Item $index')),
    );
  }),
)

方案2:支持懒加载的Builder实现(适合大量数据场景)

如果需要处理大量数据,可结合ListView.builder实现懒加载,仅渲染当前可见的行:

class ResponsiveStretchGridBuilder extends StatelessWidget {
  final double itemBaseWidth;
  final int itemCount;
  final Widget Function(BuildContext, int) itemBuilder;

  const ResponsiveStretchGridBuilder({
    super.key,
    required this.itemBaseWidth,
    required this.itemCount,
    required this.itemBuilder,
  });

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final availableWidth = constraints.maxWidth;
        final crossAxisCount = (availableWidth / itemBaseWidth).floor();
        if (crossAxisCount == 0) return const SizedBox.shrink();

        final rowCount = (itemCount / crossAxisCount).ceil();

        return ListView.builder(
          itemCount: rowCount,
          itemBuilder: (context, rowIndex) {
            final startIndex = rowIndex * crossAxisCount;
            final endIndex = startIndex + crossAxisCount > itemCount ? itemCount : startIndex + crossAxisCount;
            final rowItemCount = endIndex - startIndex;
            final isLastRow = rowIndex == rowCount - 1;

            final itemWidth = isLastRow && rowItemCount < crossAxisCount
                ? availableWidth / rowItemCount
                : itemBaseWidth;

            return Row(
              children: List.generate(rowItemCount, (colIndex) {
                final itemIndex = startIndex + colIndex;
                return SizedBox(
                  width: itemWidth,
                  child: itemBuilder(context, itemIndex),
                );
              }),
            );
          },
        );
      },
    );
  }
}

使用示例:

ResponsiveStretchGridBuilder(
  itemBaseWidth: 200,
  itemCount: 100, // 支持百级以上数据量
  itemBuilder: (context, index) {
    return Container(
      margin: const EdgeInsets.all(4),
      color: Colors.blue[100 * (index % 9)],
      height: 150,
      child: Center(child: Text('Item $index')),
    );
  },
)

核心逻辑说明

  1. 动态列数计算:通过LayoutBuilder获取容器宽度,自动计算每行能容纳的基础尺寸项数量
  2. 最后一行适配:当最后一行项数不足时,将剩余宽度平均分配给该行所有项,实现填充效果
  3. 性能优化:方案2通过ListView.builder实现懒加载,避免一次性加载大量数据导致的内存占用过高问题

内容的提问来源于stack exchange,提问作者Anas-Qasem

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 14:53:25