Flutter PageView组件详解
一、什么是PageView?
PageView是一个可以左右滑动切换页面的滚动组件,类似于Android的ViewPager或iOS的UIScrollView,常用于实现轮播图、引导页、标签页等场景。
二、基本用法
1. 基础构造函数
less
PageView(
children: [
Container(color: Colors.red),
Container(color: Colors.green),
Container(color: Colors.blue),
],
)
2. PageView.builder(常用)
dart
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("PageView"),
),
body: PageView.builder(itemBuilder: (BuildContext context,int index){
return Container(
child: Text("页面 $index"),
);
}),
),
);
}
}
执行效果如下:

3. PageView.custom
dart
import 'package:flutter/material.dart';
void main(List<String> args) {
runApp(MainPage());
}
class MainPage extends StatefulWidget {
MainPage({Key? key}) : super(key: key);
@override
_MainPageState createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text("PageView"),
),
body: PageView.custom(childrenDelegate: SliverChildBuilderDelegate((context, index) => Container(child: Text('Item$index'),),
childCount: 10
)
),
),
);
}
}
三、核心属性详解
| 属性 | 类型 | 说明 |
|---|---|---|
controller |
PageController | 控制页面跳转、获取当前页面 |
children |
List | 子页面列表 |
scrollDirection |
Axis | 滚动方向(horizontal/vertical) |
physics |
ScrollPhysics | 滚动物理效果 |
pageSnapping |
bool | 是否锁定到整页(默认true) |
onPageChanged |
ValueChanged | 页面切换回调 |
allowImplicitScrolling |
bool | 是否允许隐式滚动 |
padEnds |
bool | 是否填充两端(默认true) |
四、PageController详解
常用方法
less
PageController _controller = PageController(
initialPage: 0, // 初始页面索引
keepPage: true, // 是否保持页面状态
viewportFraction: 0.8, // 视口占比(0.8表示一屏显示80%)
);
// 跳转到指定页(带动画)
_controller.animateToPage(
2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
// 跳转到指定页(无动画)
_controller.jumpToPage(2);
// 下一页
_controller.nextPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
// 上一页
_controller.previousPage(
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
// 获取当前页面
int currentPage = _controller.page?.round() ?? 0;
五、实际应用场景
1. 轮播图(带自动播放)
less
class CarouselDemo extends StatefulWidget {
@override
_CarouselDemoState createState() => _CarouselDemoState();
}
class _CarouselDemoState extends State<CarouselDemo> {
late PageController _controller;
int _currentPage = 0;
late Timer _timer;
@override
void initState() {
super.initState();
_controller = PageController();
_startAutoPlay();
}
void _startAutoPlay() {
_timer = Timer.periodic(Duration(seconds: 3), (timer) {
if (_controller.hasClients) {
int nextPage = (_currentPage + 1) % 10;
_controller.animateToPage(
nextPage,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
});
}
@override
void dispose() {
_timer.cancel();
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
PageView.builder(
controller: _controller,
onPageChanged: (page) => setState(() => _currentPage = page),
itemCount: 10,
itemBuilder: (context, index) => Image.network(
'https://picsum.photos/id/${index + 1}/400/300',
fit: BoxFit.cover,
),
),
Positioned(
bottom: 10,
left: 0,
right: 0,
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(10, (index) => Container(
margin: EdgeInsets.symmetric(horizontal: 4),
width: 8,
height: 8,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: _currentPage == index ? Colors.white : Colors.grey,
),
)),
),
),
],
);
}
}
2. 引导页(Onboarding)
dart
复制下载
less
class OnboardingPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: PageView(
onPageChanged: (index) {
if (index == 2) {
// 最后一页,显示按钮
}
},
children: [
OnboardingItem(
title: '欢迎使用',
description: '发现精彩内容',
icon: Icons.favorite,
),
OnboardingItem(
title: '探索世界',
description: '连接全球用户',
icon: Icons.public,
),
OnboardingItem(
title: '开始体验',
description: '立即加入我们',
icon: Icons.start,
),
],
),
);
}
}
3. 垂直滚动页面(类似抖音)
dart
复制下载
less
PageView(
scrollDirection: Axis.vertical, // 垂直方向
children: [
VideoPlayerWidget(videoUrl: 'url1'),
VideoPlayerWidget(videoUrl: 'url2'),
VideoPlayerWidget(videoUrl: 'url3'),
],
)
4. 部分可见效果(类似卡片轮播)
dart
复制下载
less
PageView.builder(
controller: PageController(viewportFraction: 0.85),
itemCount: images.length,
itemBuilder: (context, index) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
image: DecorationImage(
image: NetworkImage(images[index]),
fit: BoxFit.cover,
),
),
);
},
)
六、性能优化技巧
1. 使用 builder 而非 children
dart
复制下载
scss
// ❌ 不推荐:一次性创建所有页面
PageView(children: List.generate(100, (index) => MyPage()))
// ✅ 推荐:按需创建
PageView.builder(itemCount: 100, itemBuilder: (context, index) => MyPage())
2. 缓存页面状态
dart
复制下载
less
PageView(
controller: PageController(keepPage: true), // 保持页面状态
children: [...],
)
3. 懒加载图片
dart
复制下载
css
PageView.builder(
itemBuilder: (context, index) {
return FutureBuilder(
future: precacheImage(NetworkImage(urls[index]), context),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return Image.network(urls[index]);
}
return Center(child: CircularProgressIndicator());
},
);
},
)
七、常见问题和解决方案
问题1:PageView 高度自适应
dart
复制下载
less
// 解决方案:使用 ConstrainedBox
ConstrainedBox(
constraints: BoxConstraints(maxHeight: 300),
child: PageView(children: [...]),
)
问题2:嵌套滚动冲突
dart
复制下载
less
PageView(
physics: NeverScrollableScrollPhysics(), // 禁用PageView滚动
children: [
ListView(), // 让ListView自己滚动
],
)
问题3:页面切换时保持状态
dart
复制下载
scala
PageView(
children: [
AutomaticKeepAliveClientWrapper(child: MyPage1()),
AutomaticKeepAliveClientWrapper(child: MyPage2()),
],
)
class AutomaticKeepAliveClientWrapper extends StatefulWidget {
final Widget child;
const AutomaticKeepAliveClientWrapper({required this.child});
@override
_AutomaticKeepAliveClientWrapperState createState() =>
_AutomaticKeepAliveClientWrapperState();
}
class _AutomaticKeepAliveClientWrapperState
extends State<AutomaticKeepAliveClientWrapper>
with AutomaticKeepAliveClientMixin {
@override
bool get wantKeepAlive => true;
@override
Widget build(BuildContext context) {
super.build(context);
return widget.child;
}
}
八、与其他组件的对比
| 组件 | 适用场景 | 特点 |
|---|---|---|
| PageView | 整页滑动、轮播图 | 自带滑动动画,性能好 |
| TabBarView | 配合TabBar使用 | 需要与TabBar联动 |
| ListView | 列表数据 | 支持多种item类型 |
| SingleChildScrollView | 单个可滚动内容 | 简单内容滚动 |
九、最佳实践总结
- 大量页面使用
PageView.builder,避免内存问题 - 及时释放
PageController,在 dispose 中调用 - 自动轮播时检查
hasClients,避免空指针 - 使用
AutomaticKeepAliveClientMixin保持页面状态 - 合理设置
viewportFraction实现特殊效果 - 注意嵌套滚动,设置正确的 physics