前言
原生微信的微信、通讯录、发现、我的页面不仅可以通过点击导航栏进行切换,还可以通过滑动页面内容进行切换。 我们也要实现类似的功能。
想法
- 使用Flutter的
PageView组件来完成页面的切换。
效果图
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: 拖拽行为
使用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;
}