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
相关推荐
Lanren的编程日记4 小时前
Flutter鸿蒙应用开发:数据加密功能实现实战,全方位保护用户隐私数据
flutter·华为·harmonyos
梦想不只是梦与想5 小时前
flutter 与 Android iOS 通信?以及实现原理(一)
android·flutter·ios·methodchannel·eventchannel·basicmessage
2601_949593657 小时前
Flutter OpenHarmony 三方库 path_provider 文件路径获取适配详解
flutter
哈撒Ki7 小时前
快速入门 Dart 语言
前端·flutter·dart
小蜜蜂嗡嗡8 小时前
flutter 自定义走马灯,内部为Widget控件的走马灯效果二:横向无限匀速滚动+每个Item与屏幕左侧对齐时,停靠3秒再继续滚动
开发语言·flutter
浮芷.8 小时前
生命科学数据视界防御:基于鸿蒙Flutter陀螺仪云台与三维体积光栅的视轴锁定架构
flutter·华为·架构·开源·harmonyos·鸿蒙
千码君20168 小时前
Flutter:在win10上第一次安装和尝试开发记录
flutter·gradle·android-studio·安卓模拟器
浮芷.8 小时前
微观搜打撤:基于鸿蒙flutter的内存快照算法的局内外状态隔离与高阶背包系统设计
算法·flutter·华为·开源·harmonyos·鸿蒙
浮芷.8 小时前
东方修仙模拟器:基于 鸿蒙Flutter 状态机与 CustomPainter 的境界跃升与天劫渲染架构
科技·flutter·华为·架构·开源·harmonyos·鸿蒙