PAYATHON 2026

Flutter 中 GridView 如何随 SingleChildScrollView 完整滚动?

数据小许

结论

问题代码用 SizedBox(height: 200) 把 GridView 的可见高度限制在 200。shrinkWrap: true 无法突破父级的高度约束,所以网格不会按 50 个项目的总高度展开。移除固定高度,保留 shrinkWrap: true 和 NeverScrollableScrollPhysics(),由外层 SingleChildScrollView 负责滚动。

调整代码

AllProductsWidget 可以直接返回网格,无需再用固定高度的 SizedBox 包裹:

class AllProductsWidget extends StatelessWidget {
  const AllProductsWidget({super.key, required this.isDark});

  final bool isDark;

  @override
  Widget build(BuildContext context) {
    return GridView.builder(
      gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
        maxCrossAxisExtent: 150,
      ),
      itemCount: 50,
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemBuilder: (context, index) {
        return Container(
          margin: const EdgeInsets.all(10),
          decoration: BoxDecoration(
            color: isDark ? TColors.textWhite : TColors.textBlack,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Stack(
            children: [
              Image.asset(
                'assets/images/Blender.jpg',
                height: 180,
                width: 180,
                fit: BoxFit.cover,
              ),
              Positioned(
                top: 5,
                right: 0,
                child: InkWell(
                  onTap: () {},
                  child: const Icon(Icons.favorite_border, color: Colors.amber),
                ),
              ),
              Positioned(
                bottom: 2,
                right: 2,
                child: InkWell(
                  onTap: () {},
                  child: Container(
                    decoration: const BoxDecoration(
                      color: Colors.blue,
                      borderRadius: BorderRadius.only(
                        topLeft: Radius.circular(12),
                        bottomRight: Radius.circular(12),
                      ),
                    ),
                    child: Icon(
                      Icons.add,
                      color: isDark ? Colors.white : Colors.black,
                    ),
                  ),
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

外层仍可使用 SingleChildScrollView 和 Column:

SingleChildScrollView(
  child: Column(
    children: [
      // 其他组件
      AllProductsWidget(isDark: isDark),
      // 网格下方的组件
    ],
  ),
)

itemBuilder 的参数顺序应为 (context, index):第一个参数是 BuildContext,第二个参数是项目索引。

为什么这些设置有效

  • 移除 SizedBox(height: 200) 后,网格会根据项目数量和网格布局计算所需高度。
  • shrinkWrap: true 让 GridView 在主轴方向按内容收缩,适用于放在 SingleChildScrollView 这类提供无界滚动空间的布局中。
  • NeverScrollableScrollPhysics() 禁止网格自身滚动,由外层滚动视图统一处理页面滚动。

注意事项

shrinkWrap 会增加布局计算开销。项目较多或页面需要高效懒加载时,可以改用 CustomScrollView,通过 SliverToBoxAdapter 放置普通组件,再用 SliverGrid 放置网格。当前布局适合项目数量有限、需要整页统一滚动的情况。

备注:内容仅供参考。