Flutter 专题29 图解自定义底部状态栏 ACEBottomNavigationBar#yyds干货盘点#

智者不为愚者谋,勇者不为怯者死。这篇文章主要讲述Flutter 专题29 图解自定义底部状态栏 ACEBottomNavigationBar#yyds干货盘点#相关的知识,希望能为你提供帮助。
            小菜刚接触 Flutter 时接触到底部状态栏 BottomNavigationBar 方便快捷,但随着使用过程发现依然有一些限制,包括图片选择/样式凸出/固定 NavigationItem 位等。小菜不才,准备照葫芦画瓢,自定义一个底部状态栏,并尝试封装成一个 Pub 插件。
            小菜首先了解了一下 BottomNavigationBar,主要由整体填充布局与子NavigationItem,小菜也是这样设计的,但 BottomNavigationBar 设计的配置部分主要是在 BottomNavigationBar 中完成的,而 BottomNavigationBarItem 可以看作只是一个单纯的实体类,小菜认为这样设计的好处就是统一管理,减少冗余配置等;而小菜为了配置项更多更灵活选择在 NavigationItem 中进行配置判断,这样实现的缺点就是冗余项较多,小菜也会不断学习完善。
设计尝试
一:类型确定            小菜尝试用枚举类型确定不同的样式,明确且方便,延展性也较好;

enum ACEBottomNavigationBarType { normal,// 普通类型,选中变色,样式不变 zoom,// 图片或icon变大,此时隐藏文字,支持变色 zoomout, // 图片或icon变大,并凸出显示,文字显示,支持变色 zoomoutonlypic,// 图片或icon变大,并凸出显示,文字隐藏 }

Flutter 专题29 图解自定义底部状态栏 ACEBottomNavigationBar#yyds干货盘点#

文章图片

二:NavigationItem 搭建            对于 NavigationItem 因为计划有凸出效果展示,整体用了 Stack 来搭建,配合 AnimatedAlign 等具体的组件来共同搭建,因为 Item 中各种状态均可根据用户定义的样式进行传参,故所有字段前均需 @required。
class NavigationItem extends StatelessWidget { final UniqueKey uniqueKey; final textStr; final textUnSelectedColor; final textSelectedColor; final icon; final iconUnSelectedColor; final iconSelectedColor; final image; final imageSelected; final selected; final ACEBottomNavigationBarType type; final Function(UniqueKey uniqueKey) callbackFunction; NavigationItem( {@required this.uniqueKey, @required this.selected, @required this.textStr, @required this.textSelectedColor, @required this.textUnSelectedColor, @required this.icon, @required this.iconSelectedColor, @required this.iconUnSelectedColor, @required this.image, @required this.imageSelected, @required this.callbackFunction, @required this.type}); @override Widget build(BuildContext context) { return Expanded( child: Stack(children: < Widget> [ Container( alignment: Alignment.bottomCenter, child: Opacity( opacity: textOption(), child: Padding( padding: const EdgeInsets.all(6.0), child: Text(textStr, overflow: TextOverflow.ellipsis, maxLines: 1, style: TextStyle( fontWeight: FontWeight.w600, color: selected ? textSelectedColor : textUnSelectedColor))))), Container( child: AnimatedAlign( duration: Duration(milliseconds: 0), alignment: picZoomAlignment(), child: childWid())) ])); }double picSize() { var size; if (type == ACEBottomNavigationBarType.normal) { size = 30.0; } else { size = selected ? 50.0 : 30.0; } return size; }double textOption() { var option; if (type == ACEBottomNavigationBarType.zoom || type == ACEBottomNavigationBarType.zoomoutonlypic) { option = selected ? 0.0 : 1.0; } else if (type == ACEBottomNavigationBarType.zoomout) { option = 1.0; } else { option = 1.0; } return option; }EdgeInsetsGeometry imagePadding() { EdgeInsetsGeometry edge; if (type == ACEBottomNavigationBarType.zoom) { edge = selected ? EdgeInsets.only(top: 6.0, bottom: 6.0) : EdgeInsets.only(bottom: 20.0); } else if (type == ACEBottomNavigationBarType.zoomout || type == ACEBottomNavigationBarType.zoomoutonlypic) { edge = selected ? EdgeInsets.only(bottom: 0.0) : EdgeInsets.only(bottom: 20.0); } else if (type == ACEBottomNavigationBarType.normal) { edge = EdgeInsets.only(bottom: 20.0); } else { edge = EdgeInsets.only(bottom: 0.0); } return edge; }Widget childWid() { Widget widget; if (image != null) { widget = GestureDetector( child: Padding( padding: imagePadding(), child: Image( image: (selected & & imageSelected != null) ? imageSelected : image, width: picSize(), height: picSize())), onTap: () { callbackFunction(uniqueKey); }); } else { widget = IconButton( highlightColor: Colors.transparent, splashColor: Colors.transparent, padding: EdgeInsets.only(bottom: 24.0), alignment: Alignment(0, 0), icon: Icon(icon, size: picSize(), color: selected ? iconSelectedColor : iconUnSelectedColor), onPressed: () { callbackFunction(uniqueKey); }); } return widget; } }

Flutter 专题29 图解自定义底部状态栏 ACEBottomNavigationBar#yyds干货盘点#

文章图片

三:ACEBottomNavigationBar 框架搭建
            小菜自定义 ACEBottomNavigationBar 用来装载 Item 框架,若不设置单独 Item 时使用 ACEBottomNavigationBar 配置项,为公共效果,若两者同时设置,优先使用 NavigationItem 效果。
            为了实现切换时可以对应相应的 Tab 页,需要设置 item key。
class ACEBottomNavigationBar extends StatefulWidget { final Key key; final List< NavigationItemBean> items; final initSelectedIndex; final bgColor; final bgImage; final Function(int position) onTabChangedListener; final textStr; final textUnSelectedColor; final textSelectedColor; final icon; final iconUnSelectedColor; final iconSelectedColor; final image; final imageSelected; final ACEBottomNavigationBarType type; ACEBottomNavigationBar( {@required this.items, @required this.onTabChangedListener, ACEBottomNavigationBarType type, this.key, this.initSelectedIndex = 0, this.textStr, this.textSelectedColor, this.textUnSelectedColor, this.icon, this.iconSelectedColor, this.iconUnSelectedColor, this.image, this.imageSelected, this.bgColor, this.bgImage}) : assert(onTabChangedListener != null), assert(items != null), assert(items.length > = 1 & & items.length < = 5), type = type; @override _ACEBottomNavigationBar createState() => _ACEBottomNavigationBar(); }class _ACEBottomNavigationBar extends State< ACEBottomNavigationBar> with TickerProviderStateMixin, RouteAware { var curSelectedIndex = 0; var textSelectedColor; var textUnSelectedColor; var iconSelectedColor; var iconUnSelectedColor; @override void initState() { super.initState(); _setSelected(widget.items[widget.initSelectedIndex].key); }_setSelected(UniqueKey key) { if (mounted) { setState(() { curSelectedIndex = widget.items.indexWhere((tabData) => tabData.key == key); }); } }@override void didChangeDependencies() { super.didChangeDependencies(); textUnSelectedColor = (widget.textUnSelectedColor == null) ? (Theme.of(context).brightness == Brightness.dark) ? Colors.white : Colors.black54 : widget.textUnSelectedColor; textSelectedColor = (widget.textSelectedColor == null) ? (Theme.of(context).brightness == Brightness.dark) ? Colors.white : Colors.black87 : widget.textSelectedColor; iconUnSelectedColor = (widget.iconUnSelectedColor == null) ? (Theme.of(context).brightness == Brightness.dark) ? Colors.white : Colors.black54 : widget.iconUnSelectedColor; iconSelectedColor = (widget.iconSelectedColor == null) ? (Theme.of(context).brightness == Brightness.dark) ? Colors.white : Colors.black87 : widget.iconSelectedColor; }@override Widget build(BuildContext context) { return Stack(alignment: Alignment.bottomCenter, children: < Widget> [ Container( height: 60.0, decoration: navigationBarBg(), child: Row( mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.center, children: widget.items .map((item) => NavigationItem( uniqueKey: item.key, selected: item.key == widget.items[curSelectedIndex].key, icon: item.icon, textStr: item.textStr, textSelectedColor: (item.textSelectedColor == null) ? this.textSelectedColor : item.textSelectedColor, textUnSelectedColor: (item.textUnSelectedColor == null) ? this.textUnSelectedColor : item.textUnSelectedColor, iconSelectedColor: (item.iconSelectedColor == null) ? this.iconSelectedColor : item.iconSelectedColor, iconUnSelectedColor: (item.iconUnSelectedColor == null) ? this.iconUnSelectedColor : item.iconUnSelectedColor, type: widget.type != null ? widget.type : ACEBottomNavigationBarType.normal, image: item.image, imageSelected: item.imageSelected, callbackFunction: (uniqueKey) { int selected = widget.items .indexWhere((tabData) => tabData.key == uniqueKey); widget.onTabChangedListener(selected); _setSelected(uniqueKey); })) .toList())) ]); }BoxDecoration navigationBarBg() { return widget.bgImage != null ? BoxDecoration(boxShadow: [ BoxShadow( color: Colors.black12, offset: Offset(0, -1), blurRadius: 8) ], image: DecorationImage(fit: BoxFit.cover, image: widget.bgImage)) : BoxDecoration( color: widget.bgColor != null ? widget.bgColor : Colors.white, boxShadow: [ BoxShadow( color: Colors.black12, offset: Offset(0, -1), blurRadius: 8) ]); } }

Flutter 专题29 图解自定义底部状态栏 ACEBottomNavigationBar#yyds干货盘点#

文章图片

注意事项
  1. ACEBottomNavigationBarType 为状态栏样式,默认为 nomal 类型,支持文字和图片/icon 颜色切换;
  2. 小菜尝试时对图片设置成图片和 icon 两种,icon 类型支持颜色绘制,而图片支持选中和未选中两张图切换;同时如果设置图片和 icon 两种,优先使用图片样式;同时用户对于两张图样式时可以只设置一张未选中状态图;同时支持图片和 icon 两种方式共存;
  3. 小菜设计 NavigationItem 中传递 image 图片,是为了支持本地图/网络图/内存图等多种图片格式;
  4. ACEBottomNavigationBar 中可以设置背景图或背景色,优先使用背景图效果,且背景图支持本地图或网络图。
            小菜尝试过程中还有很多欠缺,下一步计划添加固定凸出 Item 位样式,并尝试发不成 Pub 插件,有不对的地方敬请指点!
【Flutter 专题29 图解自定义底部状态栏 ACEBottomNavigationBar#yyds干货盘点#】            小菜对细节地方介绍较少,希望各位朋友优先尝试效果。

    推荐阅读