Flutter Bottom Navigation Bar
底部导航是常见的APP布局方式,实际上我自己常用的app都是底部导航的。Android和iOS都有官方组件可供使用。Flutter也有,使用时有踩坑,这里记录一下。
一般用法
普通实现:
1 | BottomNavigationBar botttomNavBar = BottomNavigationBar( |
问:看起来很简单,至于分析这么多吗?
答:emmmm,这实现优点是设计标准规范,官方组件也简单稳定可靠。但前提是设计师接受这种设定(即使是fixed,选中图标和文字也会有放大缩小动画),至少中国主流的APP,navigation item都是fixed而且没有动画,官方组件并不提供这种选择。
有点问题
既然设计师有要求那不能怂,分析是因为内部的_BottomNavigationTile作祟,那自己实现navigationItem控制是否选中,并且不传currentIndex给BottomNavigationBar,应该可以吧
1 | Widget _buildBottomNavigationBar() { |
问:效果如何?
答:嗯,不错。等等。。。啊,怎么有个大一点。没道理啊,事出蹊跷必有妖,需要从源码中找答案了。下图的home明显比mail大,对吧?
源码阅读
主要代码都在bottom_navigation_bar.dart里,bottom_navigation_bar_item.dart是item的定义
bottom_navigation_bar_item.dart
相当于是一个自定义的Button,用来放在BottomNavigationBar上,它实现了Material(Android)和Cupertino(iOS)两种风格。
bottom_navigation_bar.dart
Scaffold是Root Widget- MaterialApp的脚手架。封装了Material Design App会用到的AppBar,Drawer,SnackBar,BottomNavigationBar等。BottomNavigationBarType有fixed 和shifting两种样式,超过3个才会有区别,一般为了体验一致,我们会用fixed type。
BottomNavigationBar是一个StatefulWidget,可以按以下步骤分析这种组件:1,先看它持有的状态,2,看下他的生命周期实现,3,再仔细分析它的build方法。
- 持有状态
1 | List<AnimationController> _controllers = <AnimationController>[]; |
前面三个属性都和动画相关,第四个是设背景。
问:BottomNavigationBar为什么没有变量标记当前哪个item选中?
答:函数式编程一个原则是要函数尽量纯,currentIndex这个属性依赖外边传入,每次变化重新触发Render。如果自己维护,则还需要提供一个回调方法供外部调用,返回最新的currentIndex值。
- 生命周期方法
1 | // 初始化操作,具体实现再resetState里,对上面的这些状态属性初始化操作 |
注意:initState里有个操作比较隐蔽:_controllers[widget.currentIndex].value = 1.0;
- 分析build方法
1 |
|
- _BottomNavigationTile看下
1 | Widget _buildIcon() { |
改进实现
通过分析分析源码,发现原因是bottomNavigationBarState的initState里_controllers[widget.currentIndex].value = 1.0
设了currentIndex item动画的初值,currentIndex的默认值是0,所以第一个图标会大一点点。这个问题也有比较鸡贼的手法可以处理(魔改源码什么~),但这样大家都觉得不妥。同事眉头一皱,做了一个大胆的决定,不用系统组件BottomNavigationBar,自己封装一下:
1 | // SafeArea来兼容下iPhone X,android和iOS阴影不一样,所以区分下。 |
问:这该是最终版了吧?*
答:Naive,是连iPhone X都考虑了,但细节渐变颜色,platform特性支持还没有。。。说到特性我就佛了,一佛,我就想起西天取经,明年年初,中美合拍的西游记即将正式开机,我继续扮演美猴王孙悟空,我会用美猴王艺术形象努力创造一个正能量的形象,文体两开花,弘扬中华文化,希望大家多多关注。
一些收获
- 组件动画实现可以参考BottomNavigationBar,规范,
- 文字动画实现可以用Matrix4和Vector3,比较高级(这个在TabBar用上了),
- 考虑给官方提个issue(需求比较区域化)。
本文源码地址:https://github.com/hyjfine/flutter-play
(完)
@子路宇, 本文版权属于再惠研发团队,欢迎转载,转载请保留出处。