Flutter那些事-PageView

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 单个可滚动内容 简单内容滚动

九、最佳实践总结

  1. 大量页面使用 PageView.builder,避免内存问题
  2. 及时释放 PageController,在 dispose 中调用
  3. 自动轮播时检查 hasClients,避免空指针
  4. 使用 AutomaticKeepAliveClientMixin 保持页面状态
  5. 合理设置 viewportFraction 实现特殊效果
  6. 注意嵌套滚动,设置正确的 physics
相关推荐
GitLqr3 小时前
iOS 27 强制要求 UISceneDelegate:UIKit 和 Flutter 开发者该如何应对?
flutter·ios·全栈
蜡台7 小时前
Flutter HTTP 请求完整详解
网络协议·flutter·http·dart
9765033359 小时前
iOS 上架/审核 4.3a Cocos 2026最新方案解读
flutter·ios·swift·cocos2d·ios开发
FungLeo20 小时前
Flutter 接入 Alice 调试浮窗:一个顶层 final 抢跑,把 release 网络整没了
网络·flutter
恋猫de小郭1 天前
Flutter hit,一个可以灵活控制溢出点击的第三方包
android·前端·flutter
蜡台2 天前
Flutter Container 与装饰完整讲解
android·javascript·flutter·dart
杉氧2 天前
跨平台持久化:Flutter 本地数据库的多线程安全与架构设计实践
android·前端·flutter
yuanlaile2 天前
Flutter 开发鸿蒙 App 踩坑总结,一套完整实战学习方案分享
flutter·harmonyos·flutter开发鸿蒙·flutter开发鸿蒙实战·flutter ai实战·鸿蒙 ai实战
1001101_QIA3 天前
flutter打包流程
flutter
GitLqr3 天前
Flutter FocusNode 实战指南:玩转键盘焦点与用户输入体验
android·flutter·ios