当前位置:   article > 正文

TabBar详解_tabbar参数

tabbar参数

Flutter中提供了许多方便的UI控件,供我们进行快速的开发,本篇将对TabBar进行介绍学习。

 

简介

 

TabBar在应用中是比较常见的一个控件,通常是配合TabBarView。TabBa作导航栏,TabBarView作导航栏当前所对应的内容区。来看一下TabBar配合TabBarView使用的效果:

 

F8CE5F8885472A373E3CCF6D5ABD7600.gif

 

 

使用

 

TabBar的参数:

 

  1. const TabBar({
  2. Key key,
  3. @required this.tabs,//子标签
  4. this.controller,//控制器
  5. this.isScrollable = false,//能否滑动,false:tab宽度则等比,true:tab宽度则包裹item
  6. this.indicatorColor,//指示器颜色
  7. this.indicatorWeight = 2.0,
  8. this.indicatorPadding = EdgeInsets.zero,
  9. this.indicator,
  10. this.indicatorSize,//TabBarIndicatorSize.label:indicator与文字同宽,TabBarIndicatorSize.tab:与tab同宽
  11. this.labelColor,//选中标签颜色
  12. this.labelStyle,//选中标签样式
  13. this.labelPadding,
  14. this.unselectedLabelColor,//未选中标签颜色
  15. this.unselectedLabelStyle,
  16. this.dragStartBehavior = DragStartBehavior.down,
  17. this.onTap,//点击事件
  18. })

 

TabBar中主要有两个参数:

  • tabs:tabs属性接受一个Widget数组,表示每一个Tab子菜单,我们可以自定义,也可以像示例中一样直接使用Tab 组件,它是Material组件库提供的Material风格的Tab菜单。Tab组件有三个可选参数,除了可以指定文字外,还可以指定Tab菜单图标,或者直接自定义组件样式。

 

controller:用于控制/监听Tab菜单切换,作用于TabBar和TabBarView,负责两者之间联动。TabBar与TabBarView通过index有一一对应关系,并且默认提供了DefaultTabController建立两者之间的关系,若要切换动画以及监听切换交互,可以自定义一个 Controller。

 

TabBarView的参数:

 

  1. const TabBarView({
  2. Key key,
  3. @required this.children,
  4. this.controller,
  5. this.physics,
  6. this.dragStartBehavior = DragStartBehavior.start,
  7. });

 

这里可以看到TabBarView中也有controller参数,即上述所说通过Controller来控制二者之间联动交互。

 

这里有几个注意事项:

 

  1. 页面必须继承StatefulWidget
  2. 页面必须实现SingleTickerProviderStateMixin
  3. 页面初始化时,实例化TabController
  4. 在TabBar组件中指定controller为我们实例化的TabController
  5. 在TabBarView组件中指定controller为我们实例化的TabController

 

接下来我们看下具体在项目中是如何使用的:

 

添加Tab

  1. List<Tab> titleTabs = = <Tab>[
  2. Tab(text: '首页',
  3. icon: new Icon(Icons.android)
  4. ),
  5. Tab(text: '社群',
  6. icon: new Icon(Icons.home)
  7. ),
  8. Tab(text: '财务',
  9. icon: new Icon(Icons.accessibility)
  10. ),
  11. ];

定义controller

  1. TabController tabcontroller = TabController(
  2. length: 3, //Tab页的个数
  3. vsync: this //动画效果的异步处理,默认格式
  4. );

完成布局

 

  1. class HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
  2. //Tab页的控制器,可以用来定义Tab标签和内容页的坐标
  3. TabController tabcontroller;
  4. List<Tab> titleTabs;
  5. //生命周期方法插入渲染树时调用,只调用一次
  6. @override
  7. void initState() {
  8. super.initState();
  9. tabcontroller = TabController(
  10. length: 3, //Tab页的个数
  11. vsync: this //动画效果的异步处理,默认格式
  12. );
  13. titleTabs = <Tab>[
  14. Tab(text: '首页',
  15. icon: new Icon(Icons.android)
  16. ),
  17. Tab(text: '社群',
  18. icon: new Icon(Icons.home)
  19. ),
  20. Tab(text: '财务',
  21. icon: new Icon(Icons.accessibility)
  22. ),
  23. ];
  24. }
  25. @override
  26. Widget build(BuildContext context) {
  27. // TODO: implement build
  28. return Scaffold(
  29. appBar: AppBar(
  30. automaticallyImplyLeading:false,
  31. elevation: 1,
  32. title: Text("首页",
  33. style: TextStyle(color: Color.fromARGB(255, 51, 51, 51))),
  34. backgroundColor: Colors.white,
  35. ),
  36. body: Center(
  37. child: TabBarView(
  38. controller: tabcontroller,
  39. children: [
  40. Center(child:Text('view1')),
  41. Center(child:Text('view2')),
  42. Center(child:Text('view3')),
  43. ]),
  44. ),
  45. bottomNavigationBar: Material(
  46. color: Colors.white,
  47. child: TabBar(
  48. controller: tabcontroller,
  49. tabs: titleTabs,
  50. //tab被选中时的颜色,设置之后选中的时候,icon和text都会变色
  51. labelColor: Color.fromARGB(255, 163, 117, 136),
  52. //tab未被选中时的颜色,设置之后选中的时候,icon和text都会变色
  53. unselectedLabelColor: Colors.black,
  54. ),
  55. ),
  56. );
  57. }
  58. //组件即将销毁时调用
  59. @override
  60. void dispose() {
  61. //释放内存,节省开销
  62. tabcontroller.dispose();
  63. super.dispose();
  64. }
  65. }

 

这里注意在组件销毁的时候记得释放tabcontroller。

 

原理

 

我们通过分析Tabcontroller是如何协调TabBar和TabBarView联动这条线来深入的学习一下TabBar。

 

首先看一下TabBar内部实现:

 

  1. @override
  2. Widget build(BuildContext context) {
  3. final MaterialLocalizations localizations = MaterialLocalizations.of(context);
  4. if (_controller.length == 0) {
  5. return Container(
  6. height: _kTabHeight + widget.indicatorWeight,
  7. );
  8. }
  9. final TabBarTheme tabBarTheme = TabBarTheme.of(context);
  10. final List<Widget> wrappedTabs = List<Widget>(widget.tabs.length);
  11. for (int i = 0; i < widget.tabs.length; i += 1) {
  12. wrappedTabs[i] = Center(
  13. heightFactor: 1.0,
  14. child: Padding(
  15. padding: widget.labelPadding ?? tabBarTheme.labelPadding ?? kTabLabelPadding,
  16. child: KeyedSubtree(
  17. key: _tabKeys[i],
  18. child: widget.tabs[i],
  19. ),
  20. ),
  21. );
  22. }
  23. if (_controller != null) {
  24. final int previousIndex = _controller.previousIndex;
  25. if (_controller.indexIsChanging) {
  26. final Animation<double> animation = _ChangeAnimation(_controller);
  27. wrappedTabs[_currentIndex] = _buildStyledTab(wrappedTabs[_currentIndex], true, animation);
  28. wrappedTabs[previousIndex] = _buildStyledTab(wrappedTabs[previousIndex], false, animation);
  29. } else {
  30. final int tabIndex = _currentIndex;
  31. final Animation<double> centerAnimation = _DragAnimation(_controller, tabIndex);
  32. wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], true, centerAnimation);
  33. if (_currentIndex > 0) {
  34. final int tabIndex = _currentIndex - 1;
  35. final Animation<double> previousAnimation = ReverseAnimation(_DragAnimation(_controller, tabIndex));
  36. wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, previousAnimation);
  37. }
  38. if (_currentIndex < widget.tabs.length - 1) {
  39. final int tabIndex = _currentIndex + 1;
  40. final Animation<double> nextAnimation = ReverseAnimation(_DragAnimation(_controller, tabIndex));
  41. wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, nextAnimation);
  42. }
  43. }
  44. }
  45. final int tabCount = widget.tabs.length;
  46. for (int index = 0; index < tabCount; index += 1) {
  47. wrappedTabs[index] = InkWell(
  48. onTap: () { _handleTap(index); },
  49. child: Padding(
  50. padding: EdgeInsets.only(bottom: widget.indicatorWeight),
  51. child: Stack(
  52. children: <Widget>[
  53. wrappedTabs[index],
  54. Semantics(
  55. selected: index == _currentIndex,
  56. label: localizations.tabLabel(tabIndex: index + 1, tabCount: tabCount),
  57. ),
  58. ],
  59. ),
  60. ),
  61. );
  62. if (!widget.isScrollable)
  63. wrappedTabs[index] = Expanded(child: wrappedTabs[index]);
  64. }
  65. Widget tabBar = CustomPaint(
  66. painter: _indicatorPainter,
  67. child: _TabStyle(
  68. animation: kAlwaysDismissedAnimation,
  69. selected: false,
  70. labelColor: widget.labelColor,
  71. unselectedLabelColor: widget.unselectedLabelColor,
  72. labelStyle: widget.labelStyle,
  73. unselectedLabelStyle: widget.unselectedLabelStyle,
  74. child: _TabLabelBar(
  75. onPerformLayout: _saveTabOffsets,
  76. children: wrappedTabs,
  77. ),
  78. ),
  79. );
  80. if (widget.isScrollable) {
  81. _scrollController ??= _TabBarScrollController(this);
  82. tabBar = SingleChildScrollView(
  83. dragStartBehavior: widget.dragStartBehavior,
  84. scrollDirection: Axis.horizontal,
  85. controller: _scrollController,
  86. child: tabBar,
  87. );
  88. }
  89. return tabBar;
  90. }

 

这里主要看下onTap()点击事件,它执行了_handleTap(index);来看下_handleTap方法:

 

  1. void _handleTap(int index) {
  2. assert(index >= 0 && index < widget.tabs.length);
  3. _controller.animateTo(index);
  4. if (widget.onTap != null) {
  5. widget.onTap(index);
  6. }
  7. }

 

该方法实现比较简单,可以看到controller的身影,它执行了animateTo方法,继续看animateTo实现:

 

  1. void animateTo(int value, { Duration duration = kTabScrollDuration, Curve curve = Curves.ease }) {
  2. _changeIndex(value, duration: duration, curve: curve);
  3. }

 

这里可以看到controller是通过_changeIndex方法来进行对TabBar及TabBarView的控制:

 

  1. void _changeIndex(int value, { Duration duration, Curve curve }) {
  2. if (value == _index || length < 2)
  3. return;
  4. _previousIndex = index;
  5. _index = value;
  6. if (duration != null) {
  7. _indexIsChangingCount += 1;
  8. notifyListeners(); // Because the value of indexIsChanging may have changed.
  9. _animationController
  10. .animateTo(_index.toDouble(), duration: duration, curve: curve)
  11. .whenCompleteOrCancel(() {
  12. _indexIsChangingCount -= 1;
  13. notifyListeners();
  14. });
  15. } else {
  16. _indexIsChangingCount += 1;
  17. _animationController.value = _index.toDouble();
  18. _indexIsChangingCount -= 1;
  19. notifyListeners();
  20. }
  21. }

 

更新index,并通知TabBar及TabBarView注册的listener进行页面切换,流程还是比较简单清晰的。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/576883
推荐阅读
相关标签
  

闽ICP备14008679号