赞
踩
TabBar在应用中是比较常见的一个控件,通常是配合TabBarView。TabBa作导航栏,TabBarView作导航栏当前所对应的内容区。来看一下TabBar配合TabBarView使用的效果:
TabBar的参数:
- const TabBar({
- Key key,
- @required this.tabs,//子标签
- this.controller,//控制器
- this.isScrollable = false,//能否滑动,false:tab宽度则等比,true:tab宽度则包裹item
- this.indicatorColor,//指示器颜色
- this.indicatorWeight = 2.0,
- this.indicatorPadding = EdgeInsets.zero,
- this.indicator,
- this.indicatorSize,//TabBarIndicatorSize.label:indicator与文字同宽,TabBarIndicatorSize.tab:与tab同宽
- this.labelColor,//选中标签颜色
- this.labelStyle,//选中标签样式
- this.labelPadding,
- this.unselectedLabelColor,//未选中标签颜色
- this.unselectedLabelStyle,
- this.dragStartBehavior = DragStartBehavior.down,
- this.onTap,//点击事件
- })
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
TabBar中主要有两个参数:
tabs
属性接受一个Widget数组,表示每一个Tab子菜单,我们可以自定义,也可以像示例中一样直接使用Tab
组件,它是Material组件库提供的Material风格的Tab菜单。Tab
组件有三个可选参数,除了可以指定文字外,还可以指定Tab菜单图标,或者直接自定义组件样式。
controller:用于控制/监听Tab
菜单切换,作用于TabBar和TabBarView,负责两者之间联动。TabBar与TabBarView通过index有一一对应关系,并且默认提供了DefaultTabController建立两者之间的关系,若要切换动画以及监听切换交互,可以自定义一个 Controller。
TabBarView的参数:
- const TabBarView({
- Key key,
- @required this.children,
- this.controller,
- this.physics,
- this.dragStartBehavior = DragStartBehavior.start,
- });
这里可以看到TabBarView中也有controller参数,即上述所说通过Controller来控制二者之间联动交互。
这里有几个注意事项:
接下来我们看下具体在项目中是如何使用的:
- List<Tab> titleTabs = = <Tab>[
- Tab(text: '首页',
- icon: new Icon(Icons.android)
- ),
- Tab(text: '社群',
- icon: new Icon(Icons.home)
- ),
- Tab(text: '财务',
- icon: new Icon(Icons.accessibility)
- ),
- ];
- TabController tabcontroller = TabController(
- length: 3, //Tab页的个数
- vsync: this //动画效果的异步处理,默认格式
- );
- class HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
- //Tab页的控制器,可以用来定义Tab标签和内容页的坐标
- TabController tabcontroller;
- List<Tab> titleTabs;
-
- //生命周期方法插入渲染树时调用,只调用一次
- @override
- void initState() {
- super.initState();
- tabcontroller = TabController(
- length: 3, //Tab页的个数
- vsync: this //动画效果的异步处理,默认格式
- );
-
- titleTabs = <Tab>[
- Tab(text: '首页',
- icon: new Icon(Icons.android)
- ),
- Tab(text: '社群',
- icon: new Icon(Icons.home)
- ),
- Tab(text: '财务',
- icon: new Icon(Icons.accessibility)
- ),
- ];
- }
-
- @override
- Widget build(BuildContext context) {
- // TODO: implement build
- return Scaffold(
- appBar: AppBar(
- automaticallyImplyLeading:false,
- elevation: 1,
- title: Text("首页",
- style: TextStyle(color: Color.fromARGB(255, 51, 51, 51))),
- backgroundColor: Colors.white,
- ),
- body: Center(
- child: TabBarView(
- controller: tabcontroller,
- children: [
- Center(child:Text('view1')),
- Center(child:Text('view2')),
- Center(child:Text('view3')),
- ]),
- ),
- bottomNavigationBar: Material(
- color: Colors.white,
- child: TabBar(
- controller: tabcontroller,
- tabs: titleTabs,
- //tab被选中时的颜色,设置之后选中的时候,icon和text都会变色
- labelColor: Color.fromARGB(255, 163, 117, 136),
- //tab未被选中时的颜色,设置之后选中的时候,icon和text都会变色
- unselectedLabelColor: Colors.black,
- ),
- ),
- );
- }
-
- //组件即将销毁时调用
- @override
- void dispose() {
- //释放内存,节省开销
- tabcontroller.dispose();
- super.dispose();
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这里注意在组件销毁的时候记得释放tabcontroller。
我们通过分析Tabcontroller是如何协调TabBar和TabBarView联动这条线来深入的学习一下TabBar。
首先看一下TabBar内部实现:
- @override
- Widget build(BuildContext context) {
- final MaterialLocalizations localizations = MaterialLocalizations.of(context);
- if (_controller.length == 0) {
- return Container(
- height: _kTabHeight + widget.indicatorWeight,
- );
- }
-
- final TabBarTheme tabBarTheme = TabBarTheme.of(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: widget.tabs[i],
- ),
- ),
- );
-
- }
-
- if (_controller != null) {
- final int previousIndex = _controller.previousIndex;
-
- if (_controller.indexIsChanging) {
- final Animation<double> animation = _ChangeAnimation(_controller);
- wrappedTabs[_currentIndex] = _buildStyledTab(wrappedTabs[_currentIndex], true, animation);
- wrappedTabs[previousIndex] = _buildStyledTab(wrappedTabs[previousIndex], false, animation);
- } else {
- final int tabIndex = _currentIndex;
- final Animation<double> centerAnimation = _DragAnimation(_controller, tabIndex);
- wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], true, centerAnimation);
- if (_currentIndex > 0) {
- final int tabIndex = _currentIndex - 1;
- final Animation<double> previousAnimation = ReverseAnimation(_DragAnimation(_controller, tabIndex));
- wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, previousAnimation);
- }
- if (_currentIndex < widget.tabs.length - 1) {
- final int tabIndex = _currentIndex + 1;
- final Animation<double> nextAnimation = ReverseAnimation(_DragAnimation(_controller, tabIndex));
- wrappedTabs[tabIndex] = _buildStyledTab(wrappedTabs[tabIndex], false, nextAnimation);
- }
- }
- }
-
- final int tabCount = widget.tabs.length;
- for (int index = 0; index < tabCount; index += 1) {
- wrappedTabs[index] = InkWell(
- onTap: () { _handleTap(index); },
- child: Padding(
- padding: EdgeInsets.only(bottom: widget.indicatorWeight),
- child: Stack(
- children: <Widget>[
- wrappedTabs[index],
- Semantics(
- selected: index == _currentIndex,
- label: localizations.tabLabel(tabIndex: index + 1, tabCount: tabCount),
- ),
- ],
- ),
- ),
- );
- if (!widget.isScrollable)
- wrappedTabs[index] = Expanded(child: wrappedTabs[index]);
- }
-
- Widget tabBar = CustomPaint(
- painter: _indicatorPainter,
- child: _TabStyle(
- animation: kAlwaysDismissedAnimation,
- selected: false,
- labelColor: widget.labelColor,
- unselectedLabelColor: widget.unselectedLabelColor,
- labelStyle: widget.labelStyle,
- unselectedLabelStyle: widget.unselectedLabelStyle,
- child: _TabLabelBar(
- onPerformLayout: _saveTabOffsets,
- children: wrappedTabs,
- ),
- ),
- );
-
- if (widget.isScrollable) {
- _scrollController ??= _TabBarScrollController(this);
- tabBar = SingleChildScrollView(
- dragStartBehavior: widget.dragStartBehavior,
- scrollDirection: Axis.horizontal,
- controller: _scrollController,
- child: tabBar,
- );
- }
-
- return tabBar;
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
这里主要看下onTap()点击事件,它执行了_handleTap(index);来看下_handleTap方法:
- void _handleTap(int index) {
- assert(index >= 0 && index < widget.tabs.length);
- _controller.animateTo(index);
- if (widget.onTap != null) {
- widget.onTap(index);
- }
- }
该方法实现比较简单,可以看到controller的身影,它执行了animateTo方法,继续看animateTo实现:
- void animateTo(int value, { Duration duration = kTabScrollDuration, Curve curve = Curves.ease }) {
- _changeIndex(value, duration: duration, curve: curve);
- }
这里可以看到controller是通过_changeIndex方法来进行对TabBar及TabBarView的控制:
- void _changeIndex(int value, { Duration duration, Curve curve }) {
- if (value == _index || length < 2)
- return;
- _previousIndex = index;
- _index = value;
- if (duration != null) {
- _indexIsChangingCount += 1;
- notifyListeners(); // Because the value of indexIsChanging may have changed.
- _animationController
- .animateTo(_index.toDouble(), duration: duration, curve: curve)
- .whenCompleteOrCancel(() {
- _indexIsChangingCount -= 1;
- notifyListeners();
- });
- } else {
- _indexIsChangingCount += 1;
- _animationController.value = _index.toDouble();
- _indexIsChangingCount -= 1;
- notifyListeners();
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
更新index,并通知TabBar及TabBarView注册的listener进行页面切换,流程还是比较简单清晰的。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。