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
相关推荐
WaywardOne13 小时前
Flutter面试事件队列,微任务队列以及事件循环相关问题及回答
flutter·面试
明君8799715 小时前
Flutter 包体积优化实战:从 175MB 到 105MB
flutter
zeqinjie18 小时前
Skills-Flutter 内测泄漏审核
前端·flutter·app
UnicornDev20 小时前
【Flutter x HarmonyOS 6】魔方计时APP——记录页面的UI设计
flutter·ui·华为·harmonyos·鸿蒙
用户50523720991521 小时前
Dart 3.x 完全指南:Records / Patterns / Class Modifiers(2026)
flutter
折翅鵬2 天前
Flutter Accordion 完全指南:从折叠面板入门到高级样式自定义
flutter
jiejiejiejie_2 天前
Flutter for OpenHarmony 渐变色UI设计实战:LinearGradient与RadialGradient深度应用
flutter·ui
xmdy58662 天前
Flutter+开源鸿蒙实战|城市共享驿站智能存取系统 Day1 项目初始化+架构分层+多端适配+全局状态基座
flutter·开源·harmonyos