1419 字
7 分钟
Flutter Day04: GridView 与 CustomScrollView

Flutter Day04:GridView 与 CustomScrollView 知识点#

1. GridView 组件概述#

GridView 用于展示二维网格列表,适合图片墙、商品列表等网格布局页面。

核心特点:

  • 支持滚动。
  • 支持网格布局(按列数或按最大宽度)。
  • 支持按需渲染(懒加载),只构建当前可见区域的 item,离开可视区域的 item 会被回收,性能更好。

2. GridView.count 模式(按列数布局)#

常用属性#

  • crossAxisCount:每行(垂直滚动时)的列数。
  • mainAxisSpacing:主轴方向 item 间距。
  • crossAxisSpacing:交叉轴方向 item 间距。
  • childAspectRatio:item 宽高比(宽 / 高)。
  • padding:GridView 内边距。
  • scrollDirection:滚动方向,默认 Axis.vertical。
  • children:直接传入子组件列表。

示例代码#

class MyGridView1 extends StatefulWidget {
const MyGridView1({super.key});
@override
State<MyGridView1> createState() => _MyGridViewState1();
}
class _MyGridViewState1 extends State<MyGridView1> {
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("GridView.count")),
body: GridView.count(
controller: _scrollController,
scrollDirection: Axis.vertical,
crossAxisCount: 3,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 1,
padding: const EdgeInsets.all(20),
children: List.generate(100, (index) {
return Container(
color: Colors.blue,
alignment: Alignment.center,
child: Text(
"Item $index",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
}),
),
),
);
}
}

3. GridView.extent 模式(按最大宽度布局)#

GridView.extent 通过 maxCrossAxisExtent 控制 item 最大宽度,列数会根据屏幕宽度自动变化。

例如:

  • item 最大宽度为 100。
  • 屏幕足够宽时每行可放 3 个。
  • 屏幕变窄后会自动降为 2 个或 1 个。

常用属性#

  • maxCrossAxisExtent:每个 item 在交叉轴方向的最大尺寸。
  • mainAxisSpacing:主轴方向 item 间距。
  • crossAxisSpacing:交叉轴方向 item 间距。
  • childAspectRatio:item 宽高比。
  • padding:内边距。

示例代码#

class MyGridView2 extends StatefulWidget {
const MyGridView2({super.key});
@override
State<MyGridView2> createState() => _MyGridViewState2();
}
class _MyGridViewState2 extends State<MyGridView2> {
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("GridView.extent")),
body: GridView.extent(
controller: _scrollController,
scrollDirection: Axis.vertical,
maxCrossAxisExtent: 100,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 1,
padding: const EdgeInsets.all(20),
children: List.generate(100, (index) {
return Container(
color: Colors.blue,
alignment: Alignment.center,
child: Text(
"Item $index",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
}),
),
),
);
}
}

4. GridView.builder 模式(推荐大数据量)#

GridView.builder 适合大量数据场景,按需构建 item。

常用属性#

  • itemBuilder:构建每个 item 的回调,参数为 context 和 index。
  • itemCount:item 总数量。
  • gridDelegate:网格布局规则。
  • padding:内边距。
  • scrollDirection:滚动方向。

gridDelegate 常见类型#

  • SliverGridDelegateWithFixedCrossAxisCount:按固定列数布局。
  • SliverGridDelegateWithMaxCrossAxisExtent:按最大宽度布局。

示例代码#

class MyGridView3 extends StatefulWidget {
const MyGridView3({super.key});
@override
State<MyGridView3> createState() => _MyGridViewState3();
}
class _MyGridViewState3 extends State<MyGridView3> {
final ScrollController _scrollController = ScrollController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("GridView.builder")),
body: GridView.builder(
controller: _scrollController,
scrollDirection: Axis.vertical,
padding: const EdgeInsets.all(20),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
childAspectRatio: 1,
),
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.blue,
alignment: Alignment.center,
child: Text(
"Item $index",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
},
),
),
);
}
}

5. CustomScrollView 组件概述#

CustomScrollView 用于组合多个可滚动区域,并支持更灵活的滚动效果(如轮播图、吸顶分类、混合列表等)。

关键点:

  • slivers 参数中只能放 Sliver 组件。
  • 不能直接放普通 Container(可用 SliverToBoxAdapter 包裹)。
  • 常见 Sliver:
    • SliverToBoxAdapter:包裹普通组件。
    • SliverList:列表。
    • SliverGrid:网格。
    • SliverPersistentHeader:可配置吸顶头部。

轮播图相关状态与属性#

  • _currentPage:当前轮播页索引,用于更新分页小圆点颜色。
  • PageController:控制 PageView 翻页。
  • onPageChanged:监听手势滑动后的页码变化。
  • animateToPage:点击小圆点时平滑跳转到指定页。

SliverPersistentHeader 相关属性#

  • pinned: true:吸顶固定。
  • floating: false:不会在反向滚动时立即浮现。
  • delegate:头部布局和行为委托。

示例代码#

class MyCustomScrollView extends StatefulWidget {
const MyCustomScrollView({super.key});
@override
State<MyCustomScrollView> createState() => _MyCustomScrollViewState();
}
class _MyCustomScrollViewState extends State<MyCustomScrollView> {
int _currentPage = 0;
final PageController _pageController = PageController();
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("CustomScrollView")),
body: CustomScrollView(
slivers: [
SliverToBoxAdapter(
child: Stack(
children: [
Container(
height: 260,
color: Colors.blue,
alignment: Alignment.center,
child: PageView.builder(
controller: _pageController,
onPageChanged: (index) {
setState(() {
_currentPage = index;
});
},
itemBuilder: (BuildContext context, int index) {
return Container(
color: Colors.blue,
alignment: Alignment.center,
child: Text(
"轮播图 $index",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
},
itemCount: 10,
),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
height: 30,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(10, (index) {
return GestureDetector(
onTap: () {
_pageController.animateToPage(
index,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
setState(() {
_currentPage = index;
});
},
child: Container(
width: 10,
height: 10,
margin: const EdgeInsets.symmetric(horizontal: 10),
decoration: BoxDecoration(
color: _currentPage == index ? Colors.red : Colors.grey,
shape: BoxShape.circle,
),
),
);
}),
),
),
],
),
),
const SliverToBoxAdapter(
child: SizedBox(height: 20),
),
SliverPersistentHeader(
delegate: _StickyCategory(),
floating: false,
pinned: true,
),
SliverList.separated(
itemCount: 100,
itemBuilder: (BuildContext context, int index) {
return Container(
alignment: Alignment.center,
width: double.infinity,
height: 100,
color: Colors.blue,
child: Text(
"Item $index",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
},
separatorBuilder: (BuildContext context, int index) {
return const SizedBox(height: 10);
},
),
],
),
),
);
}
}

6. SliverPersistentHeaderDelegate 吸顶头部#

通过自定义 SliverPersistentHeaderDelegate,可以实现吸顶分类栏。

关键方法与属性#

  • build(context, shrinkOffset, overlapsContent):构建头部 UI。
    • shrinkOffset:头部收缩量。
    • overlapsContent:是否与下方内容重叠。
  • maxExtent:头部最大高度。
  • minExtent:头部最小高度。
  • shouldRebuild(oldDelegate):是否需要重建。
    • 头部配置不变时返回 false,可提升性能。

示例代码#

class _StickyCategory extends SliverPersistentHeaderDelegate {
@override
Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
return Container(
color: Colors.white,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: 10,
itemBuilder: (BuildContext context, int index) {
return Container(
alignment: Alignment.center,
width: 100,
height: 60,
margin: const EdgeInsets.symmetric(horizontal: 10),
color: Colors.blue,
child: Text(
"分类 $index",
style: const TextStyle(color: Colors.white, fontSize: 20),
),
);
},
),
);
}
@override
double get maxExtent => 80;
@override
double get minExtent => 60;
@override
bool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) {
return false;
}
}

7. PageView 轮播补充知识点#

在你的代码里,轮播图通过 PageView.builder 放在 SliverToBoxAdapter 中,再用 Stack + Positioned 叠加分页指示器。

这种实现的优点:

  • 轮播区域属于整页滚动的一部分,可与下方 Sliver 一起滚动。
  • 指示器覆盖在轮播图底部,UI 更接近电商首页效果。
  • 手势滑动与点击圆点都能更新 _currentPage,交互一致。

建议补充:

  • 在 State 中重写 dispose,释放 _pageController,避免内存泄漏。
@override
void dispose() {
_pageController.dispose();
super.dispose();
}

8. 选型建议#

  • 数据量小、结构固定:优先 GridView.count。
  • 需要自适应列数:使用 GridView.extent。
  • 数据量大或来自接口分页:使用 GridView.builder。
  • 页面有复杂滚动交互(吸顶、折叠、混合布局):使用 CustomScrollView + Sliver。
Flutter Day04: GridView 与 CustomScrollView
https://zgq1008.github.io/posts/flutter/flutter_day04/
作者
Telus
发布于
2026-04-06
许可协议
CC BY-NC-SA 4.0
Flutter 系列导航