如何使用Sliver实现可横向滚动的Flutter网格视图
在Flutter的CustomScrollView中实现横向滚动的SliverGrid
问题描述
我需要在CustomScrollView的slivers数组里实现一个横向滚动的网格视图,目前代码只能显示成纵向列表。试过用嵌套Container的方式实现横向网格,但想通过动态生成(比如list.generate)构建子项却没成功,而且我的CustomScrollView中还有其他滚动列表组件。
当前可运行但显示为纵向的代码:
body: CustomScrollView( slivers: [ SliverPadding( padding: EdgeInsets.symmetric(horizontal: 16.0), sliver: SliverGrid( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 2, crossAxisSpacing: 16, mainAxisSpacing: 16, crossAxisCount: 2, ), delegate: SliverChildBuilderDelegate( (BuildContext context, int i) { return InkWell( onTap: () {}, child: ClipRRect( clipBehavior: Clip.antiAlias, borderRadius: BorderRadius.circular(8), child: Container( height: 200, width: double.infinity, decoration: BoxDecoration( image: DecorationImage( image: CachedNetworkImageProvider('categoryImage'), fit: BoxFit.cover, onError: (exception, stackTrace) => LoadingImage(), ), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0x1FFFFFFF), Color(0x463B3B3B), ], ), borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.shade900, offset: const Offset(1, 1), spreadRadius: 1, blurRadius: 50, blurStyle: BlurStyle.outer), ]), child: Container( padding: const EdgeInsets.all(10), height: 100, color: Colors.transparent, alignment: Alignment.bottomLeft, width: double.infinity, child: Text( "title", overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodyText1!, ), ), ), ), ); }, childCount: count.length, ), ), ), ], )
解决方案
SliverGrid本身会跟随CustomScrollView的纵向滚动,要实现横向网格,需要把横向滚动的网格包装成适配sliver的组件。以下是修改后的完整代码:
body: CustomScrollView( slivers: [ // 这里可以保留你原有的其他sliver组件 SliverPadding( padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 16), sliver: SliverToBoxAdapter( child: SingleChildScrollView( scrollDirection: Axis.horizontal, child: SizedBox( // 计算总宽度:屏幕宽度 × (item总数 / 列数),这里列数是2 width: MediaQuery.of(context).size.width * (count.length / 2), child: GridView.builder( shrinkWrap: true, // 关闭GridView自身的滚动,避免和外层横向滚动冲突 physics: const NeverScrollableScrollPhysics(), gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( childAspectRatio: 2, crossAxisSpacing: 16, mainAxisSpacing: 16, crossAxisCount: 2, ), itemCount: count.length, itemBuilder: (BuildContext context, int i) { return InkWell( onTap: () {}, child: ClipRRect( clipBehavior: Clip.antiAlias, borderRadius: BorderRadius.circular(8), child: Container( decoration: BoxDecoration( image: DecorationImage( image: CachedNetworkImageProvider('categoryImage'), fit: BoxFit.cover, onError: (exception, stackTrace) => const LoadingImage(), ), gradient: const LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [ Color(0x1FFFFFFF), Color(0x463B3B3B), ], ), borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.shade900, offset: const Offset(1, 1), spreadRadius: 1, blurRadius: 50, blurStyle: BlurStyle.outer, ), ], ), child: Container( padding: const EdgeInsets.all(10), alignment: Alignment.bottomLeft, child: Text( "title", overflow: TextOverflow.ellipsis, style: Theme.of(context).textTheme.bodyText1!, ), ), ), ), ); }, ), ), ), ), ), // 这里可以添加其他sliver组件 ], )
关键修改说明
- SliverToBoxAdapter:把非sliver类型的横向滚动组件转换成sliver,才能加入
CustomScrollView的slivers数组 - SingleChildScrollView:设置
scrollDirection: Axis.horizontal提供横向滚动能力 - SizedBox:给GridView设置固定宽度,确保横向滚动的范围正确,计算公式根据列数调整
- GridView.builder:
shrinkWrap: true:让GridView根据内容高度自适应,避免不必要的滚动physics: NeverScrollableScrollPhysics():关闭自身滚动,防止和外层横向滚动冲突
- 动态生成子项:通过
itemBuilder和itemCount实现动态渲染,和你之前的逻辑一致,也可以替换成List.generate构建子列表(比如children: List.generate(count.length, (i) => ...))
内容的提问来源于stack exchange,提问作者Digamber negi
相关产品推荐
相关产品推荐

