Flutter TabBar、TabBarView、 TabController 实现 Tab 标签菜单布局
一、TabBar 和 TabBarView
Tab 标签栏的基本组成是顶部的 Tab标题和内容区域,在 Flutter 中,Material 提供了 TabBar 和 TabBarView 两个 Widget
1、TabBar
TabBar
构造函数如下,其中只有 tabs
是必须的,接收一个 List<Tab>
列表,indicator 相关的都是标题的下划线相关的样式配置,而 label 则是文字的相关样式
const TabBar({
Key key,
@required this.tabs,
this.controller,
this.isScrollable = false,
this.indicatorColor,
this.indicatorWeight = 2.0,
this.indicatorPadding = EdgeInsets.zero,
this.indicator,
this.indicatorSize,
this.labelColor,
this.labelStyle,
this.labelPadding,
this.unselectedLabelColor,
this.unselectedLabelStyle,
this.dragStartBehavior = DragStartBehavior.start,
this.onTap,
})
如果你在页面上直接使用 TabBar
或者 TabBarView 可能会得到以下错误:
TabBar
定义中也说了:
/// If a [TabController] is not provided, then there must be a
/// [DefaultTabController] ancestor.
TabBar 和 TabBarView 都必须放在一个开发者提供的 TabController 或者 DefaultTabController
(默认的 TabController)中
body: DefaultTabController(
length: 9,
child: Column(
children: <Widget>[
Container(
color: Colors.pink,
child: TabBar(
labelColor: Colors.yellow,
unselectedLabelColor: Colors.white,
indicatorWeight: 1,
isScrollable: true,
// labelPadding: EdgeInsets.fromLTRB(10, 0, 0, 0),
labelStyle: TextStyle(fontSize: 14),
tabs: <Widget>[
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
Tab(text: 'Tab1'),
],
),
)
],
),
)
上面代码中,body 里面就是 DefaultTabController,然后将 TabBar
放在其中
最终的效果:
2、TabBarView
有了 TabBar
只要在创建相对应数目的 TabBarView 即可,与 TabBar 相对应
TabBarView
构造相对简单,毕竟只是个特殊的容器:
/// Creates a page view with one child per tab.
///
/// The length of [children] must be the same as the [controller]'s length.
const TabBarView({
Key key,
@required this.children,
this.controller,
this.physics,
this.dragStartBehavior = DragStartBehavior.start,
})
容器中其实可以放置任何我们想要放置的内容,比如创建一个 ListView:
class ListViewContnet extends StatelessWidget {
const ListViewContnet({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView(
children: <Widget>[
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
ListTile(title: Text(TITLE)),
],
);
}
}
然后在上面的 DefaultTabController
中加上 TabBarView
Expanded
中使用 TabBarView。
比如上面示例中,TabBar 已经放在了 Column 中,因此 整个剩下的 Column 空间都是用来给 TabBarView 使用的:
Expanded(
flex: 1,
child: TabBarView(
children: <Widget>[
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
],
),
)
Expanded 设置 flex:1
,Column 空间剩下的都给 TabBarView 使用,最终效果:
完整代码在文章最后有链接。
二、tabController 实现的 Tab 菜单栏
上面虽然实现了一个 tab 菜单栏效果,但实际上我们无法控制,拿不到事件,比如在实际使用项目中,切换不同的 Tab 会重新请求数据,而上面的实现则不行
TabBar 的构造函数中,有一个 controller
的参数,定义如下:
/// This widget's selection and animation state.
///
/// If [TabController] is not provided, then the value of [DefaultTabController.of]
/// will be used.
final TabController controller;
这个和 TextInpute 的 InputeController 差不多一样的意思,通过一个 Controller 能够在一个 Widget class 中随意的挥霍和控制 TabController.
创建一个 TabController
非常简单:
this._tabController = new TabController(vsync: this, length: 5);
this._tabController.addListener(() {
print(this._tabController.toString());
print(this._tabController.index);
print(this._tabController.length);
print(this._tabController.previousIndex);
});
.addListenter
可以对 TabController 增加监听,每次发生切换,都能够走到方法中
TabController
,比如混入一个 SingleTickerProviderStateMixin
class _TabControllerDemoState extends State<TabControllerDemo> with SingleTickerProviderStateMixin {
除此之外,使用方式和上面差不多,只不过需要在 TabBar
和 TabBarView
中都传入生成的 controller
title: TabBar(
controller: this._tabController,
tabs: <Widget>[
Tab(text: '女装'),
Tab(text: '男装'),
Tab(text: '童装'),
Tab(text: '夏装'),
Tab(text: '冬装'),
],
)
body: TabBarView(
controller: this._tabController,
children: <Widget>[
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
],
));
三、Appbar + TabBar 实现的布局
最开始提到了,TabBarView 必须有一个固定宽高,而一般为了撑满页面,我们会选择使用 Column 这个 Widget,通过 Expanded 实现宽度高度自适应
初次之外,还可以借助 Scalfold
实现,因为 Scalfold 本身就能够撑满整个屏幕,appbar 在上面,而 body 则是其他部分
根据这个布局,TabBar
可以放在 Scalfold.appBar
中,而 TabBarView
则是放在 Scalfold.TabBarView
中
Scalfold 本身还是可以嵌套第二个 Scalfol,因此对页面本身没有什么影响。
比如借助 Scalfold 和 TabController
实现的布局:
class TabControllerDemo extends StatefulWidget {
TabControllerDemo({Key key}) : super(key: key);
_TabControllerDemoState createState() => _TabControllerDemoState();
}
class _TabControllerDemoState extends State<TabControllerDemo> with SingleTickerProviderStateMixin {
TabController _tabController;
@override
void initState() {
super.initState();
this._tabController = new TabController(vsync: this, length: 5);
this._tabController.addListener(() {
print(this._tabController.toString());
print(this._tabController.index);
print(this._tabController.length);
print(this._tabController.previousIndex);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.black,
title: TabBar(
controller: this._tabController,
tabs: <Widget>[
Tab(text: '女装'),
Tab(text: '男装'),
Tab(text: '童装'),
Tab(text: '夏装'),
Tab(text: '冬装'),
],
),
),
body: TabBarView(
controller: this._tabController,
children: <Widget>[
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
ListViewContnet(),
],
));
}
}
最终效果:
不过因为 Matedial 的 AppBar 样式可以设置的不多,因此并不是很推荐放在 AppBar 中
四、完整代码
1、TabBar 和 TabBarView
https://github.com/postbird/FlutterHelloWorldDemo/blob/dev1/demo1/lib/bak/main.31-TabBar.dart
2、TabController
文章版权:Postbird-There I am , in the world more exciting!
本文链接:http://www.ptbird.cn/flutter-tab-tabcontroller.html
转载请注明文章原始出处 !
写的很棒 谢谢你