TabBar 是基本每个App都会使用到的一个控件,在官方内置的 TabBar 的高度只有两种规格且是不可修改的:
//未设置 Icon 时的高度 const double _kTabHeight = 46.0; //设置 Icon 之后的高度 const double _kTextAndIconTabHeight = 72.0;标题与Icon之间的距离也是写死的:
margin: const EdgeInsets.only(bottom: 10.0),Tab高度/文本与Icon的距离 设置详见 class Tab 源码:
class Tab extends StatelessWidget { const Tab({ Key key, this.text, this.icon, this.child, }) : assert(text != null || child != null || icon != null), assert(!(text != null && null != child)), super(key: key); final String text; final Widget child; final Widget icon; Widget _buildLabelText() { return child ?? Text(text, softWrap: false, overflow: TextOverflow.fade); } @override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); double height; Widget label; if (icon == null) {//当没有设置 Icon 时,Tab 高度 height 取值 _kTabHeight height = _kTabHeight; label = _buildLabelText(); } else if (text == null && child == null) {//当没有设置 text 和 child 时,Tab 高度 height 取值 _kTabHeight height = _kTabHeight; label = icon; } else {//其它情况, Tab 高度 height 取值 _kTextAndIconTabHeight height = _kTextAndIconTabHeight; label = Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Container( child: icon, margin: const EdgeInsets.only(bottom: 10.0),//这里设置的是 text 和 Icon 的距离 ), _buildLabelText(), ], ); } return SizedBox( height: height, child: Center( child: label, widthFactor: 1.0, ), ); } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(StringProperty('text', text, defaultValue: null)); properties.add(DiagnosticsProperty<Widget>('icon', icon, defaultValue: null)); } }但是很多时候,我们会需要调整一下TabBar的高度或者标题文本与Icon之间的距离以达到我们UI的要求,而内置的TabBar是无法自定义设置这些参数的,这时候我们就要使出我们的绝招:魔改。 首先上一下我的魔改效果:
第一步,把TabBar源码复制一份,为了避免和系统内置的TabBar冲突,把复制的文件里面的class都改个名,比如我 把 class Tab 改为 class ZTab、class TabBar 改为 class ZTabBar 等等
第二步,class ZTab 新增我们需要的属性 :
class ZTab extends StatelessWidget { const ZTab({ Key key, this.text, this.icon, this.child, this.tabHeight = _kTabHeight, this.textToIconMargin = 0.0, }) : assert(text != null || child != null || icon != null), assert(!(text != null && null != child)), assert(textToIconMargin >= 0.0), super(key: key); /// Tab高度,默认 _kTabHeight final double tabHeight; /// Tab 文本与图标的距离,默认 0.0 final double textToIconMargin; ... }然后在 class ZTab 的 build 里面进行设置:
@override Widget build(BuildContext context) { assert(debugCheckHasMaterial(context)); double height; Widget label; if (icon == null) { height = _kTabHeight; label = _buildLabelText(); } else if (text == null && child == null) { height = _kTabHeight; label = icon; } else { height = _kTextAndIconTabHeight; label = Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Container( child: icon, ///这里设置文字与图片的距离 ///使用自定义的 textToIconMargin margin: EdgeInsets.only(bottom: textToIconMargin), ), _buildLabelText(), ], ); } ///如果Tab自定义的高度大于 _kTabHeight 则 Tab 使用自定义的高度 ///我在这里做了限制,限制条件可以自己设置 if (tabHeight > _kTabHeight) { height = tabHeight; } return SizedBox( height: height, child: Center( child: label, widthFactor: 1.0, ), ); }以上修改就可以实现TabBar高度和文本与Icon距离的自定义了,但是在使用的时候,每个Tab都需要设置tabHeight和textToIconMargin,相当繁琐且可能造成设置不统一:
TabBar( controller: _tabController, tabs: [ ZTab(text: "Home", icon: Icon(Icons.home, size: 27,), tabHeight: 54.0, textToIconMargin: 0.0,), ZTab(text: "Business", icon: Icon(Icons.business, size: 27,), tabHeight: 54.0, textToIconMargin: 0.0,), ZTab(text: "Me", icon: Icon(Icons.person, size: 27,), tabHeight: 54.0, textToIconMargin: 0.0,), ] )为了方便使用和减少错误,继续改造
新增 Tab 辅助类 ZTabHelper:
///Tab 辅助类 class ZTabHelper { const ZTabHelper({ this.text, this.icon, this.child, }) : assert(text != null || child != null || icon != null), assert(!(text != null && null != child)); final String text; final Widget child; final Widget icon; }修改 class ZTabBar:
class ZTabBar extends StatefulWidget implements PreferredSizeWidget { const ZTabBar({ Key key, ... @required this.tabs, this.tabHeight = _kTabHeight, this.textToIconMargin = 0.0, }) : assert(tabs != null), assert(isScrollable != null), assert(dragStartBehavior != null), assert(indicator != null || (indicatorWeight != null && indicatorWeight > 0.0)), assert(indicator != null || (indicatorPadding != null)), super(key: key); /// Tab高度 和 文字与图标的距离 统一提取到 ZTabBar 里面设置 /// Tab高度 final double tabHeight; /// Tab 文字与图标的距离 final double textToIconMargin; /// 从直接设置 Tab 列表改为设置我们自定义的 ZTabHelper final List<ZTabHelper> tabs; ... }修改 @override Size get preferredSize:
@override Size get preferredSize { for (ZTabHelper item in tabs) { if (item is ZTab) { final ZTabHelper tab = item; if (tab.text != null && tab.icon != null) { ///做一下判断,是否使用的是自定义高度 if (tabHeight > _kTabHeight) { return Size.fromHeight(tabHeight + indicatorWeight); } else { return Size.fromHeight(_kTextAndIconTabHeight + indicatorWeight); } } } } ///做一下判断,是否使用的是自定义高度 if (tabHeight > _kTabHeight) { return Size.fromHeight(tabHeight + indicatorWeight); } else { return Size.fromHeight(_kTabHeight + indicatorWeight); } }修改 class _ZTabBarState
class _ZTabBarState extends State<ZTabBar> { ... ///新增一个存放Tab的列表 List<Widget> _tabs = <Widget>[]; @override void initState() { ///首先判断 _textToIconMargin, 如果 _textToIconMargin 小于 0 会报错 double _textToIconMargin = widget.textToIconMargin; if (_textToIconMargin < 0) { _textToIconMargin = 0.0; } ///循环创建 Tab ///必须在 super.initState(); 之前创建好 Tab 列表,不然 build 的时候报错 widget.tabs.forEach((zTabHelper){ _tabs.add(ZTab(text: zTabHelper.text, icon: zTabHelper.icon, child: zTabHelper.child, tabHeight: widget.tabHeight, textToIconMargin: _textToIconMargin,)); }); _tabKeys = _tabs.map((Widget tab) => GlobalKey()).toList(); super.initState(); } ... @override Widget build(BuildContext context) { ... final List<Widget> wrappedTabs = List<Widget>(widget.tabs.length); for (int i = 0; i < widget.tabs.length; i += 1) { wrappedTabs[i] = Center( heightFactor: 1.0, child: Padding( padding: widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding, child: KeyedSubtree( key: _tabKeys[i], child: _tabs[i],//改为我们在initState里面创建的tab列表 ), ), ); } ... return tabBar; } }如此,一个可实现 高度 和 标题与图标距离 自定义的TabBar就改造好了。 使用:
ZTabBar( controller: _tabController, tabHeight: 54.0,//自定义Tab高度 textToIconMargin: 0.0,//自定义标题与图标距离 tabs: [ ZTabHelper(text: "Home", icon: Icon(Icons.home, size: 27,),), ZTabHelper(text: "Business", icon: Icon(Icons.business, size: 27,),), ZTabHelper(text: "Me", icon: Icon(Icons.person, size: 27,),), ] )解决 iPhone X 等刘海屏手机导航栏/底部黑线遮挡布局的方法 改变 TabBar 背景颜色的方法 源码:https://github.com/zhumj/flutter_ZTabBar.git