Flutter伪微信-004-使用PageView滑动切换页面

404 阅读2分钟

前言

原生微信的微信、通讯录、发现、我的页面不仅可以通过点击导航栏进行切换,还可以通过滑动页面内容进行切换。 我们也要实现类似的功能。

想法

  • 使用Flutter的PageView组件来完成页面的切换。

效果图

chrome-capture-2023-1-14.gif

PageView 简介

PageView的构造器如下:

PageView({
  super.key,
  this.scrollDirection = Axis.horizontal,
  this.reverse = false,
  PageController? controller,
  this.physics,
  this.pageSnapping = true,
  this.onPageChanged,
  List<Widget> children = const <Widget>[],
  this.dragStartBehavior = DragStartBehavior.start,
  this.allowImplicitScrolling = false,
  this.restorationId,
  this.clipBehavior = Clip.hardEdge,
  this.scrollBehavior,
  this.padEnds = true,
})

其中常用的属性为:

  • scrollDirection: 滑动方向
  • reverse: 是否和阅读方向一样的滚动,比如中文的阅读习惯系从左往右
  • controller: 控制器
  • physics: 页面视图如何响应用户输入
  • pageSnapping: 使用自定义滚动时禁止页面捕捉
  • onPageChanged: 页面切换回调
  • children: 页面(组件)列表,页面个数等于长度
  • dragStartBehavior: 拖拽行为

chrome-capture-2023-1-14.gif

使用PageView的三种方式

1.使用PageView构造器

具体使用如上

2.使用PageView.build方法

PageView.builder({
    //...
    @required IndexedWidgetBuilder itemBuilder,  //创建item,根据回调index返回不同页面
    int itemCount,  //item数量
    //...
  })

3.PageView.custom方法

PageView.custom({
    //...
    @required this.childrenDelegate,  //自定义模式接受一个子页面委托对象
    //...
  })

PageController介绍

PageController构造器如下:

PageController({
  this.initialPage = 0,
  this.keepPage = true,
  this.viewportFraction = 1.0,
})

其中常用的属性为:

  • initialPage: 初始页下标
  • keepPage: 是否保持已经渲染过得页面
  • viewportFraction: 每个页面应占用的视口部分。 0~1 其中常用的方法为:
  • animateToPage:带动画跳转
  • jumpToPage:直接改变当前页面无动画
  • nextPage:下一页
  • previousPage:上一页

实现

import 'package:flutter/material.dart';
import 'package:pseudo_we_chat/pages/directory/directory.dart';
import 'package:pseudo_we_chat/pages/discover/discover.dart';
import 'package:pseudo_we_chat/pages/home/home.dart';
import 'package:pseudo_we_chat/pages/message/message.dart';
import 'package:pseudo_we_chat/ui/font/WeChatFont.dart';

class IndexPage extends StatefulWidget {
  // 定义初始化导航栏选择的下标,默认为0
  final int index;

  const IndexPage({Key? key, this.index = 0}) : super(key: key);

  @override
  State<IndexPage> createState() => _IndexPageState();
}

class _IndexPageState extends State<IndexPage> {
  late int _index;

  //定义页面控制器,可以左右滑动切换页面
  PageController pageController = PageController(initialPage: 0);

  // 重写初始化状态方法进行下标赋值
  @override
  void initState() {
    super.initState();
    _index = widget.index;
  }

  // 定义导航栏组件集合
  final List<Widget> _pageItem = const [
    MessagePage(),
    DirectoryPage(),
    DiscoverPage(),
    HomePage(),
  ];

  // 定义图标Map(方便后期使用自定义图标库)
  final Map<String, Icon> _iconMap = const {
    "message": Icon(WeChatFont.message),
    "directory": Icon(WeChatFont.directory),
    "discover": Icon(WeChatFont.discover),
    "home": Icon(WeChatFont.my),
  };

  // 定义导航栏组件的数据
  final List<NavBarItem> _navBarItem = const [
    NavBarItem(icon: "message", label: "微信"),
    NavBarItem(icon: "directory", label: "通讯录"),
    NavBarItem(icon: "discover", label: "发现"),
    NavBarItem(icon: "home", label: "我的"),
  ];

  // 导航栏按钮按下事件
  void _onBottomNavigationBarTapped(index) {
    pageController.animateToPage(index,
        duration: const Duration(milliseconds: 300), curve: Curves.ease);
  }

  // 页面切换事件
  void _onPageChanged(index) {
    // 下标重新赋值,进行切换
    setState(() {
      _index = index;
    });
  }

  // 自定义构建导航栏组件的方法 - 代码复用
  List<BottomNavigationBarItem> _getNavBarItem() {
    return _navBarItem
        .map((e) => BottomNavigationBarItem(
            icon: _iconMap[e.icon] ?? const Icon(Icons.search), label: e.label))
        .toList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: PageView.builder(
            onPageChanged: _onPageChanged,
            controller: pageController,
            itemCount: _pageItem.length,
            itemBuilder: (BuildContext context, int index) {
              return _pageItem[index];
            }),
        bottomNavigationBar: BottomNavigationBar(
          //设置选中图标颜色
          selectedItemColor: Colors.green,
          //设置未选中图标颜色
          unselectedItemColor: Colors.grey,
          //设置选中文字大小
          selectedLabelStyle: const TextStyle(fontSize: 10),
          //设置未选中文字大小
          unselectedLabelStyle: const TextStyle(fontSize: 10),
          //设置类型
          type: BottomNavigationBarType.fixed,
          //设置选中下标
          currentIndex: _index,
          //设置按钮按下事件
          onTap: _onBottomNavigationBarTapped,
          //调用构建导航栏组件方法
          items: _getNavBarItem(),
        ));
  }
}

/// 自定义导航栏组件数据结构
class NavBarItem {
  const NavBarItem({required this.icon, required this.label});

  /// 图标
  final String icon;

  /// 名称
  final String label;
}

项目链接

github.com/BadKid90s/p…