Flutter PageView 页面视图深度解析:从基础到高级

通过丰富的图表、对比分析和实际案例,全面掌握 Flutter PageView 的使用技巧

📊 文章概览

章节 内容 难度等级
[基础 PageView](#章节 内容 难度等级 基础 PageView 基础页面视图实现 ⭐⭐ PageView.builder 动态页面构建 ⭐⭐⭐ PageController 控制 页面控制器使用 ⭐⭐⭐ 高级特性 高级功能实现 ⭐⭐⭐⭐ 实际应用场景 真实项目案例 ⭐⭐⭐⭐ "#%E5%9F%BA%E7%A1%80-pageview") 基础页面视图实现 ⭐⭐
[PageView.builder](#章节 内容 难度等级 基础 PageView 基础页面视图实现 ⭐⭐ PageView.builder 动态页面构建 ⭐⭐⭐ PageController 控制 页面控制器使用 ⭐⭐⭐ 高级特性 高级功能实现 ⭐⭐⭐⭐ 实际应用场景 真实项目案例 ⭐⭐⭐⭐ "#pageviewbuilder") 动态页面构建 ⭐⭐⭐
[PageController 控制](#章节 内容 难度等级 基础 PageView 基础页面视图实现 ⭐⭐ PageView.builder 动态页面构建 ⭐⭐⭐ PageController 控制 页面控制器使用 ⭐⭐⭐ 高级特性 高级功能实现 ⭐⭐⭐⭐ 实际应用场景 真实项目案例 ⭐⭐⭐⭐ "#pagecontroller-%E6%8E%A7%E5%88%B6") 页面控制器使用 ⭐⭐⭐
[高级特性](#章节 内容 难度等级 基础 PageView 基础页面视图实现 ⭐⭐ PageView.builder 动态页面构建 ⭐⭐⭐ PageController 控制 页面控制器使用 ⭐⭐⭐ 高级特性 高级功能实现 ⭐⭐⭐⭐ 实际应用场景 真实项目案例 ⭐⭐⭐⭐ "#%E9%AB%98%E7%BA%A7%E7%89%B9%E6%80%A7") 高级功能实现 ⭐⭐⭐⭐
[实际应用场景](#章节 内容 难度等级 基础 PageView 基础页面视图实现 ⭐⭐ PageView.builder 动态页面构建 ⭐⭐⭐ PageController 控制 页面控制器使用 ⭐⭐⭐ 高级特性 高级功能实现 ⭐⭐⭐⭐ 实际应用场景 真实项目案例 ⭐⭐⭐⭐ "#%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF") 真实项目案例 ⭐⭐⭐⭐

🎯 学习目标

  • ✅ 掌握 PageView 的核心概念和使用方法
  • ✅ 学会 PageController 的配置和控制
  • ✅ 理解页面切换动画和手势处理
  • ✅ 能够实现复杂的页面视图应用
  • ✅ 掌握性能优化和最佳实践

📋 目录导航

🎯 快速导航

  • [基础 PageView](#基础 PageView "#%E5%9F%BA%E7%A1%80-pageview") - 基础页面视图实现
  • PageView.builder - 动态页面构建
  • [PageController 控制](#PageController 控制 "#pagecontroller-%E6%8E%A7%E5%88%B6") - 页面控制器使用
  • 高级特性 - 高级功能实现
  • 实际应用场景 - 真实项目案例
  • 性能优化 - 最佳实践

📋 概述

PageView 是 Flutter 中用于实现页面滑动切换的重要控件,支持水平和垂直方向的页面切换,常用于引导页、轮播图、图片浏览等场景。

🏗️ PageView 架构图

graph TD A[Flutter PageView System] --> B[PageView Container] A --> C[PageController] A --> D[Page Management] A --> E[Gesture Handling] B --> F[Scroll Direction] B --> G[Page Transition] B --> H[Scroll Physics] C --> I[Page Index Control] C --> J[Animation Control] C --> K[Viewport Fraction] D --> L[Page Building] D --> M[Lazy Loading] D --> N[Page Caching] E --> O[Touch Gestures] E --> P[Swipe Detection] E --> Q[Gesture Conflicts] F --> R[Horizontal Scroll] F --> S[Vertical Scroll] G --> T[Page Animation] G --> U[Transition Effects] G --> V[Custom Transitions] H --> W[ClampingScrollPhysics] H --> X[BouncingScrollPhysics] H --> Y[Custom Physics]

📊 PageView 特性对比

特性 PageView PageView.builder PageView.custom
性能 ⭐⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
内存占用 ⭐⭐ ⭐⭐⭐⭐⭐ ⭐⭐⭐⭐
灵活性 ⭐⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐
使用复杂度 ⭐⭐⭐ ⭐⭐⭐⭐
适用场景 固定页面 动态页面 自定义需求

基础 PageView

1. 简单的 PageView

dart 复制代码
import 'package:flutter/material.dart';

class BasicPageViewExample extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('基础 PageView')),
      body: PageView(
        children: [
          Container(
            color: Colors.red[100],
            child: Center(
              child: Text(
                '第一页',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
            ),
          ),
          Container(
            color: Colors.green[100],
            child: Center(
              child: Text(
                '第二页',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
            ),
          ),
          Container(
            color: Colors.blue[100],
            child: Center(
              child: Text(
                '第三页',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

2. 使用 PageView.builder

dart 复制代码
class PageViewBuilderExample extends StatelessWidget {
  final List<Color> colors = [
    Colors.red,
    Colors.green,
    Colors.blue,
    Colors.orange,
    Colors.purple,
    Colors.teal,
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('PageView.builder')),
      body: PageView.builder(
        itemCount: colors.length,
        itemBuilder: (context, index) {
          return Container(
            color: colors[index].withOpacity(0.3),
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  Icon(
                    Icons.pages,
                    size: 64,
                    color: colors[index],
                  ),
                  SizedBox(height: 16),
                  Text(
                    '页面 ${index + 1}',
                    style: TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                      color: colors[index],
                    ),
                  ),
                  SizedBox(height: 8),
                  Text(
                    '这是第 ${index + 1} 个页面',
                    style: TextStyle(
                      fontSize: 16,
                      color: colors[index].withOpacity(0.8),
                    ),
                  ),
                ],
              ),
            ),
          );
        },
      ),
    );
  }
}

带控制器的 PageView

1. 手动控制页面切换

dart 复制代码
class ControlledPageView extends StatefulWidget {
  @override
  _ControlledPageViewState createState() => _ControlledPageViewState();
}

class _ControlledPageViewState extends State<ControlledPageView> {
  late PageController _pageController;
  int _currentPage = 0;

  final List<PageData> pages = [
    PageData('欢迎', '欢迎使用我们的应用', Icons.waving_hand, Colors.blue),
    PageData('功能', '探索强大的功能特性', Icons.star, Colors.green),
    PageData('开始', '立即开始您的旅程', Icons.rocket_launch, Colors.orange),
  ];

  @override
  void initState() {
    super.initState();
    _pageController = PageController(initialPage: 0);
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('控制器 PageView')),
      body: Column(
        children: [
          // 页面指示器
          Container(
            padding: EdgeInsets.all(16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(pages.length, (index) {
                return Container(
                  margin: EdgeInsets.symmetric(horizontal: 4),
                  width: _currentPage == index ? 12 : 8,
                  height: 8,
                  decoration: BoxDecoration(
                    color: _currentPage == index
                        ? Colors.blue
                        : Colors.grey[300],
                    borderRadius: BorderRadius.circular(4),
                  ),
                );
              }),
            ),
          ),
          // PageView
          Expanded(
            child: PageView.builder(
              controller: _pageController,
              itemCount: pages.length,
              onPageChanged: (index) {
                setState(() {
                  _currentPage = index;
                });
              },
              itemBuilder: (context, index) {
                return _buildPage(pages[index]);
              },
            ),
          ),
          // 控制按钮
          Container(
            padding: EdgeInsets.all(16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                ElevatedButton(
                  onPressed: _currentPage > 0 ? _previousPage : null,
                  child: Text('上一页'),
                ),
                Text('${_currentPage + 1} / ${pages.length}'),
                ElevatedButton(
                  onPressed: _currentPage < pages.length - 1 ? _nextPage : null,
                  child: Text('下一页'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildPage(PageData pageData) {
    return Container(
      padding: EdgeInsets.all(32),
      color: pageData.color.withOpacity(0.1),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            pageData.icon,
            size: 80,
            color: pageData.color,
          ),
          SizedBox(height: 32),
          Text(
            pageData.title,
            style: TextStyle(
              fontSize: 28,
              fontWeight: FontWeight.bold,
              color: pageData.color,
            ),
          ),
          SizedBox(height: 16),
          Text(
            pageData.description,
            style: TextStyle(
              fontSize: 16,
              color: Colors.grey[600],
            ),
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  }

  void _nextPage() {
    _pageController.nextPage(
      duration: Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );
  }

  void _previousPage() {
    _pageController.previousPage(
      duration: Duration(milliseconds: 300),
      curve: Curves.easeInOut,
    );
  }
}

class PageData {
  final String title;
  final String description;
  final IconData icon;
  final Color color;

  PageData(this.title, this.description, this.icon, this.color);
}

2. 自动轮播 PageView

dart 复制代码
class AutoPlayPageView extends StatefulWidget {
  @override
  _AutoPlayPageViewState createState() => _AutoPlayPageViewState();
}

class _AutoPlayPageViewState extends State<AutoPlayPageView> {
  late PageController _pageController;
  late Timer _timer;
  int _currentPage = 0;

  final List<BannerData> banners = [
    BannerData('轮播图1', '这是第一张轮播图', 'https://via.placeholder.com/400x200/FF5722/FFFFFF?text=Banner+1'),
    BannerData('轮播图2', '这是第二张轮播图', 'https://via.placeholder.com/400x200/4CAF50/FFFFFF?text=Banner+2'),
    BannerData('轮播图3', '这是第三张轮播图', 'https://via.placeholder.com/400x200/2196F3/FFFFFF?text=Banner+3'),
    BannerData('轮播图4', '这是第四张轮播图', 'https://via.placeholder.com/400x200/9C27B0/FFFFFF?text=Banner+4'),
  ];

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
    _startAutoPlay();
  }

  @override
  void dispose() {
    _timer.cancel();
    _pageController.dispose();
    super.dispose();
  }

  void _startAutoPlay() {
    _timer = Timer.periodic(Duration(seconds: 3), (timer) {
      if (_currentPage < banners.length - 1) {
        _currentPage++;
      } else {
        _currentPage = 0;
      }

      _pageController.animateToPage(
        _currentPage,
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('自动轮播 PageView')),
      body: Column(
        children: [
          // 轮播图
          Container(
            height: 200,
            child: PageView.builder(
              controller: _pageController,
              itemCount: banners.length,
              onPageChanged: (index) {
                setState(() {
                  _currentPage = index;
                });
              },
              itemBuilder: (context, index) {
                return _buildBanner(banners[index]);
              },
            ),
          ),
          // 指示器
          Container(
            padding: EdgeInsets.symmetric(vertical: 16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(banners.length, (index) {
                return GestureDetector(
                  onTap: () {
                    _pageController.animateToPage(
                      index,
                      duration: Duration(milliseconds: 300),
                      curve: Curves.easeInOut,
                    );
                  },
                  child: Container(
                    margin: EdgeInsets.symmetric(horizontal: 4),
                    width: _currentPage == index ? 24 : 8,
                    height: 8,
                    decoration: BoxDecoration(
                      color: _currentPage == index
                          ? Colors.blue
                          : Colors.grey[300],
                      borderRadius: BorderRadius.circular(4),
                    ),
                  ),
                );
              }),
            ),
          ),
          // 控制按钮
          Padding(
            padding: EdgeInsets.all(16),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () {
                    _timer.cancel();
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('自动播放已停止')),
                    );
                  },
                  child: Text('停止自动播放'),
                ),
                ElevatedButton(
                  onPressed: () {
                    _timer.cancel();
                    _startAutoPlay();
                    ScaffoldMessenger.of(context).showSnackBar(
                      SnackBar(content: Text('自动播放已开始')),
                    );
                  },
                  child: Text('开始自动播放'),
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildBanner(BannerData banner) {
    return Container(
      margin: EdgeInsets.symmetric(horizontal: 8),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8),
        boxShadow: [
          BoxShadow(
            color: Colors.black26,
            blurRadius: 4,
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: ClipRRect(
        borderRadius: BorderRadius.circular(8),
        child: Stack(
          fit: StackFit.expand,
          children: [
            // 背景图片(这里用颜色代替)
            Container(
              color: Colors.primaries[banners.indexOf(banner) % Colors.primaries.length],
            ),
            // 文字覆盖层
            Container(
              decoration: BoxDecoration(
                gradient: LinearGradient(
                  begin: Alignment.topCenter,
                  end: Alignment.bottomCenter,
                  colors: [
                    Colors.transparent,
                    Colors.black54,
                  ],
                ),
              ),
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.end,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      banner.title,
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    SizedBox(height: 4),
                    Text(
                      banner.description,
                      style: TextStyle(
                        color: Colors.white70,
                        fontSize: 14,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class BannerData {
  final String title;
  final String description;
  final String imageUrl;

  BannerData(this.title, this.description, this.imageUrl);
}

垂直 PageView

1. 垂直滑动页面

dart 复制代码
class VerticalPageView extends StatefulWidget {
  @override
  _VerticalPageViewState createState() => _VerticalPageViewState();
}

class _VerticalPageViewState extends State<VerticalPageView> {
  int _currentPage = 0;

  final List<StoryData> stories = [
    StoryData('故事1', '这是一个关于勇气的故事...', Icons.favorite, Colors.red),
    StoryData('故事2', '这是一个关于友谊的故事...', Icons.people, Colors.blue),
    StoryData('故事3', '这是一个关于梦想的故事...', Icons.star, Colors.orange),
    StoryData('故事4', '这是一个关于成长的故事...', Icons.trending_up, Colors.green),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('垂直 PageView')),
      body: Row(
        children: [
          // 侧边指示器
          Container(
            width: 60,
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: List.generate(stories.length, (index) {
                return GestureDetector(
                  onTap: () {
                    // 这里可以添加跳转到指定页面的逻辑
                  },
                  child: Container(
                    margin: EdgeInsets.symmetric(vertical: 8),
                    width: 40,
                    height: 40,
                    decoration: BoxDecoration(
                      color: _currentPage == index
                          ? stories[index].color
                          : Colors.grey[300],
                      shape: BoxShape.circle,
                    ),
                    child: Icon(
                      stories[index].icon,
                      color: _currentPage == index
                          ? Colors.white
                          : Colors.grey[600],
                      size: 20,
                    ),
                  ),
                );
              }),
            ),
          ),
          // 垂直 PageView
          Expanded(
            child: PageView.builder(
              scrollDirection: Axis.vertical,
              itemCount: stories.length,
              onPageChanged: (index) {
                setState(() {
                  _currentPage = index;
                });
              },
              itemBuilder: (context, index) {
                return _buildStoryPage(stories[index]);
              },
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildStoryPage(StoryData story) {
    return Container(
      padding: EdgeInsets.all(24),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
          colors: [
            story.color.withOpacity(0.1),
            story.color.withOpacity(0.3),
          ],
        ),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            story.icon,
            size: 80,
            color: story.color,
          ),
          SizedBox(height: 32),
          Text(
            story.title,
            style: TextStyle(
              fontSize: 28,
              fontWeight: FontWeight.bold,
              color: story.color,
            ),
          ),
          SizedBox(height: 16),
          Text(
            story.content,
            style: TextStyle(
              fontSize: 16,
              color: Colors.grey[700],
              height: 1.5,
            ),
            textAlign: TextAlign.center,
          ),
          SizedBox(height: 32),
          Text(
            '向上滑动查看下一个故事',
            style: TextStyle(
              fontSize: 12,
              color: Colors.grey[500],
            ),
          ),
        ],
      ),
    );
  }
}

class StoryData {
  final String title;
  final String content;
  final IconData icon;
  final Color color;

  StoryData(this.title, this.content, this.icon, this.color);
}

高级 PageView 功能

1. 自定义页面切换效果

dart 复制代码
class CustomTransitionPageView extends StatefulWidget {
  @override
  _CustomTransitionPageViewState createState() => _CustomTransitionPageViewState();
}

class _CustomTransitionPageViewState extends State<CustomTransitionPageView> {
  late PageController _pageController;
  double _currentPageValue = 0.0;

  final List<Color> colors = [
    Colors.red,
    Colors.green,
    Colors.blue,
    Colors.orange,
    Colors.purple,
  ];

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
    _pageController.addListener(() {
      setState(() {
        _currentPageValue = _pageController.page ?? 0.0;
      });
    });
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('自定义切换效果')),
      body: PageView.builder(
        controller: _pageController,
        itemCount: colors.length,
        itemBuilder: (context, index) {
          return _buildTransformPage(index);
        },
      ),
    );
  }

  Widget _buildTransformPage(int index) {
    double value = 1.0;
    if (_pageController.position.haveDimensions) {
      value = (_currentPageValue - index).abs();
      value = (1 - (value * 0.3)).clamp(0.0, 1.0);
    }

    return Transform.scale(
      scale: value,
      child: Container(
        margin: EdgeInsets.all(16),
        decoration: BoxDecoration(
          color: colors[index],
          borderRadius: BorderRadius.circular(16),
          boxShadow: [
            BoxShadow(
              color: colors[index].withOpacity(0.3),
              blurRadius: 10,
              offset: Offset(0, 5),
            ),
          ],
        ),
        child: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(
                Icons.pages,
                size: 64,
                color: Colors.white,
              ),
              SizedBox(height: 16),
              Text(
                '页面 ${index + 1}',
                style: TextStyle(
                  fontSize: 24,
                  fontWeight: FontWeight.bold,
                  color: Colors.white,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

2. 无限循环 PageView

dart 复制代码
class InfinitePageView extends StatefulWidget {
  @override
  _InfinitePageViewState createState() => _InfinitePageViewState();
}

class _InfinitePageViewState extends State<InfinitePageView> {
  late PageController _pageController;

  final List<ImageData> images = [
    ImageData('图片1', Icons.image, Colors.red),
    ImageData('图片2', Icons.photo, Colors.green),
    ImageData('图片3', Icons.camera_alt, Colors.blue),
    ImageData('图片4', Icons.photo_camera, Colors.orange),
  ];

  @override
  void initState() {
    super.initState();
    // 从中间位置开始,实现无限循环的效果
    _pageController = PageController(initialPage: images.length * 1000);
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('无限循环 PageView')),
      body: PageView.builder(
        controller: _pageController,
        itemBuilder: (context, index) {
          // 使用模运算实现循环
          final realIndex = index % images.length;
          return _buildImagePage(images[realIndex], realIndex);
        },
      ),
    );
  }

  Widget _buildImagePage(ImageData imageData, int index) {
    return Container(
      margin: EdgeInsets.all(16),
      decoration: BoxDecoration(
        color: imageData.color.withOpacity(0.1),
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: imageData.color, width: 2),
      ),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            imageData.icon,
            size: 80,
            color: imageData.color,
          ),
          SizedBox(height: 16),
          Text(
            imageData.title,
            style: TextStyle(
              fontSize: 24,
              fontWeight: FontWeight.bold,
              color: imageData.color,
            ),
          ),
          SizedBox(height: 8),
          Text(
            '索引: $index',
            style: TextStyle(
              fontSize: 16,
              color: Colors.grey[600],
            ),
          ),
          SizedBox(height: 16),
          Text(
            '可以无限向左右滑动',
            style: TextStyle(
              fontSize: 14,
              color: Colors.grey[500],
            ),
          ),
        ],
      ),
    );
  }
}

class ImageData {
  final String title;
  final IconData icon;
  final Color color;

  ImageData(this.title, this.icon, this.color);
}

实际应用案例

1. 应用引导页

dart 复制代码
class OnboardingPageView extends StatefulWidget {
  @override
  _OnboardingPageViewState createState() => _OnboardingPageViewState();
}

class _OnboardingPageViewState extends State<OnboardingPageView> {
  late PageController _pageController;
  int _currentPage = 0;

  final List<OnboardingData> onboardingPages = [
    OnboardingData(
      '欢迎使用',
      '感谢您选择我们的应用,让我们开始这段美妙的旅程',
      Icons.waving_hand,
      Colors.blue,
    ),
    OnboardingData(
      '强大功能',
      '我们为您提供了丰富的功能,让您的体验更加完美',
      Icons.star,
      Colors.green,
    ),
    OnboardingData(
      '简单易用',
      '直观的界面设计,让您轻松上手,享受流畅的操作体验',
      Icons.touch_app,
      Colors.orange,
    ),
    OnboardingData(
      '开始使用',
      '一切准备就绪,现在就开始探索我们的应用吧!',
      Icons.rocket_launch,
      Colors.purple,
    ),
  ];

  @override
  void initState() {
    super.initState();
    _pageController = PageController();
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: [
            // 跳过按钮
            Align(
              alignment: Alignment.topRight,
              child: Padding(
                padding: EdgeInsets.all(16),
                child: TextButton(
                  onPressed: _skipOnboarding,
                  child: Text('跳过'),
                ),
              ),
            ),
            // PageView
            Expanded(
              child: PageView.builder(
                controller: _pageController,
                itemCount: onboardingPages.length,
                onPageChanged: (index) {
                  setState(() {
                    _currentPage = index;
                  });
                },
                itemBuilder: (context, index) {
                  return _buildOnboardingPage(onboardingPages[index]);
                },
              ),
            ),
            // 指示器和按钮
            Padding(
              padding: EdgeInsets.all(24),
              child: Column(
                children: [
                  // 页面指示器
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: List.generate(onboardingPages.length, (index) {
                      return AnimatedContainer(
                        duration: Duration(milliseconds: 300),
                        margin: EdgeInsets.symmetric(horizontal: 4),
                        width: _currentPage == index ? 24 : 8,
                        height: 8,
                        decoration: BoxDecoration(
                          color: _currentPage == index
                              ? onboardingPages[index].color
                              : Colors.grey[300],
                          borderRadius: BorderRadius.circular(4),
                        ),
                      );
                    }),
                  ),
                  SizedBox(height: 24),
                  // 下一步/完成按钮
                  SizedBox(
                    width: double.infinity,
                    height: 50,
                    child: ElevatedButton(
                      onPressed: _nextPage,
                      style: ElevatedButton.styleFrom(
                        backgroundColor: onboardingPages[_currentPage].color,
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(25),
                        ),
                      ),
                      child: Text(
                        _currentPage == onboardingPages.length - 1
                            ? '开始使用'
                            : '下一步',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                          color: Colors.white,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildOnboardingPage(OnboardingData data) {
    return Padding(
      padding: EdgeInsets.all(32),
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            data.icon,
            size: 120,
            color: data.color,
          ),
          SizedBox(height: 48),
          Text(
            data.title,
            style: TextStyle(
              fontSize: 28,
              fontWeight: FontWeight.bold,
              color: data.color,
            ),
            textAlign: TextAlign.center,
          ),
          SizedBox(height: 24),
          Text(
            data.description,
            style: TextStyle(
              fontSize: 16,
              color: Colors.grey[600],
              height: 1.5,
            ),
            textAlign: TextAlign.center,
          ),
        ],
      ),
    );
  }

  void _nextPage() {
    if (_currentPage < onboardingPages.length - 1) {
      _pageController.nextPage(
        duration: Duration(milliseconds: 300),
        curve: Curves.easeInOut,
      );
    } else {
      _completeOnboarding();
    }
  }

  void _skipOnboarding() {
    _completeOnboarding();
  }

  void _completeOnboarding() {
    // 这里可以导航到主页面或保存引导完成状态
    Navigator.of(context).pushReplacement(
      MaterialPageRoute(builder: (context) => MainApp()),
    );
  }
}

class OnboardingData {
  final String title;
  final String description;
  final IconData icon;
  final Color color;

  OnboardingData(this.title, this.description, this.icon, this.color);
}

// 主应用页面(示例)
class MainApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('主应用')),
      body: Center(
        child: Text(
          '欢迎来到主应用!',
          style: TextStyle(fontSize: 24),
        ),
      ),
    );
  }
}

2. 图片浏览器

dart 复制代码
class ImageGalleryPageView extends StatefulWidget {
  final List<String> imageUrls;
  final int initialIndex;

  ImageGalleryPageView({
    required this.imageUrls,
    this.initialIndex = 0,
  });

  @override
  _ImageGalleryPageViewState createState() => _ImageGalleryPageViewState();
}

class _ImageGalleryPageViewState extends State<ImageGalleryPageView> {
  late PageController _pageController;
  late int _currentIndex;

  @override
  void initState() {
    super.initState();
    _currentIndex = widget.initialIndex;
    _pageController = PageController(initialPage: widget.initialIndex);
  }

  @override
  void dispose() {
    _pageController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      appBar: AppBar(
        backgroundColor: Colors.black,
        title: Text(
          '${_currentIndex + 1} / ${widget.imageUrls.length}',
          style: TextStyle(color: Colors.white),
        ),
        iconTheme: IconThemeData(color: Colors.white),
        actions: [
          IconButton(
            icon: Icon(Icons.share),
            onPressed: _shareImage,
          ),
          IconButton(
            icon: Icon(Icons.download),
            onPressed: _downloadImage,
          ),
        ],
      ),
      body: PageView.builder(
        controller: _pageController,
        itemCount: widget.imageUrls.length,
        onPageChanged: (index) {
          setState(() {
            _currentIndex = index;
          });
        },
        itemBuilder: (context, index) {
          return _buildImagePage(widget.imageUrls[index], index);
        },
      ),
      bottomNavigationBar: Container(
        color: Colors.black,
        padding: EdgeInsets.all(16),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            IconButton(
              icon: Icon(Icons.zoom_in, color: Colors.white),
              onPressed: _zoomIn,
            ),
            IconButton(
              icon: Icon(Icons.zoom_out, color: Colors.white),
              onPressed: _zoomOut,
            ),
            IconButton(
              icon: Icon(Icons.rotate_right, color: Colors.white),
              onPressed: _rotateImage,
            ),
            IconButton(
              icon: Icon(Icons.info, color: Colors.white),
              onPressed: _showImageInfo,
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildImagePage(String imageUrl, int index) {
    return InteractiveViewer(
      panEnabled: true,
      boundaryMargin: EdgeInsets.all(20),
      minScale: 0.5,
      maxScale: 4.0,
      child: Center(
        child: Container(
          decoration: BoxDecoration(
            color: Colors.grey[300],
            borderRadius: BorderRadius.circular(8),
          ),
          child: Icon(
            Icons.image,
            size: 200,
            color: Colors.grey[600],
          ),
          // 在实际应用中,这里应该是 Image.network(imageUrl)
        ),
      ),
    );
  }

  void _shareImage() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('分享图片 ${_currentIndex + 1}')),
    );
  }

  void _downloadImage() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('下载图片 ${_currentIndex + 1}')),
    );
  }

  void _zoomIn() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('放大图片')),
    );
  }

  void _zoomOut() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('缩小图片')),
    );
  }

  void _rotateImage() {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('旋转图片')),
    );
  }

  void _showImageInfo() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text('图片信息'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text('图片编号: ${_currentIndex + 1}'),
            Text('总数量: ${widget.imageUrls.length}'),
            Text('URL: ${widget.imageUrls[_currentIndex]}'),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('确定'),
          ),
        ],
      ),
    );
  }
}

最佳实践

1. 性能优化

  • 懒加载 : 使用 PageView.builder 实现按需构建
  • 内存管理: 及时释放 PageController
  • 图片优化: 对于图片轮播,使用适当的缓存策略

2. 用户体验

  • 平滑动画: 使用合适的动画曲线和持续时间
  • 视觉反馈: 提供清晰的页面指示器
  • 手势支持: 支持滑动、点击等多种交互方式

3. 可访问性

  • 语义化: 为页面内容提供清晰的语义描述
  • 键盘导航: 支持键盘切换页面
  • 屏幕阅读器: 确保内容可被屏幕阅读器正确识别

总结

PageView 是 Flutter 中实现页面滑动切换的核心组件,通过合理的配置和自定义,可以创建出丰富多样的页面切换效果。在实际应用中,要注意性能优化和用户体验的平衡,特别是在处理大量页面或复杂内容时。

相关推荐
苦逼的搬砖工4 小时前
Flutter 其他组件:让交互更丰富
flutter
tangweiguo030519876 小时前
Flutter性能优化完全指南:构建流畅应用的实用策略
android·flutter
LinXunFeng19 小时前
Flutter - 详情页初始锚点与优化
前端·flutter·开源
苦逼的搬砖工20 小时前
Flutter 文本输入:让用户与你的应用对话
flutter
苦逼的搬砖工20 小时前
Flutter Text 组件深度解析:从入门到精通
flutter
苦逼的搬砖工20 小时前
# 🎨 Flutter UI 组件深度指南
flutter
君赏1 天前
Petrel(雨燕) 新一代 Flutter Web 热更新框架.
flutter
会煮咖啡的猫1 天前
Cursor 0.46 Flutter 开发者首选
flutter
会煮咖啡的猫1 天前
Flutter GetX 4.7.2 发布:被严重低估的强大框架
flutter