Flutter TabBar、TabBarView的使用
效果:
说实话,这个效果实现起来并没有我想象中的那么简单,demo跟实际开发差的还是蛮大的,一方面是学习成本,要查看源码,另一方面就是页面的布局,层级嵌套的太深了。。
简介
TabBar
和TabBarView
到底是什么关系呢,简而言之,TabBar就是导航栏,TabBarView就是导航栏当前所对应的内容区。
TabBar
简单看一下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,//点击事件
})
参数挺多,但是没什么复杂的,都是一些基本属性。
TabBarView
简单看一下TabBarView的参数
const TabBarView({
Key key,
@required this.children,//子widget
this.controller,//控制器
this.physics,
this.dragStartBehavior = DragStartBehavior.down,
})
TabBarView的参数就更少了,因为它只是一个容器而已
TabBar和TabBarView联动
TabBar和TabBarView联动主要是控制器controller
,上面可以看到,两个widget都有一个共同的参数controller,它就是控制二者联动的。
系统提供了一个DefaultTabController
,只需要把TabBar和TabBarView包裹起来就能实现联动
const DefaultTabController({
Key key,
@required this.length,
this.initialIndex = 0,
@required this.child,
})
示例:
return new DefaultTabController(
length: _datas.length,
child: new Scaffold(
appBar: new TabBar(
...
),
body: new TabBarView(
...
),
),
);
当然,我们也可以自定义Controller
- 首先添加
SingleTickerProviderStateMixin
class _ProjectPageState extends State<ProjectPage> with SingleTickerProviderStateMixin { }
- 定义我们的Controller并初始化
class _ProjectPageState extends State<ProjectPage> with SingleTickerProviderStateMixin {
TabController controller;//tab控制器
int _currentIndex = 0; //选中下标
@override
void initState() {
super.initState();
//初始化controller并添加监听
controller = TabController(length: _datas.length, vsync: this);
controller.addListener(() => _onTabChanged());
}
}
- 重新配置TabBar和TabBarView
return new Scaffold(
appBar: new TabBar(
controller: controller,//控制器
...
),
body: new TabBarView(
controller: controller,
...
),
);
点击更新
在开发实战中,数据都是动态的,即每次点击tab标签,页面数据是要刷新的,
获取数据是一样的,只是tab下标在变,所以要在事件触发之后就更新下标。
上面我们在自定义的controller中添加了一个Listener监听tab变化,
_onTabChanged() {
if (_controller.index.toDouble() == _controller.animation.value) {
//赋值 并更新数据
this.setState(() {
_currentIndex = controller.index;
});
getDetail();
}
}
这个_currentIndex
就是标识当前选中的下标,默认选中第一个为0,
在事件中把改变的下标赋值给_currentIndex,然后根据下标重新请求刷新数据源,然后页面会重绘,即刷新。
完整代码
class ProjectPage extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new _ProjectPageState();
}
}
class _ProjectPageState extends State<ProjectPage> with SingleTickerProviderStateMixin {
TabController controller;//tab控制器
int _currentIndex = 0; //选中下标
List<ProjectData> _datas = new List();//tab集合
List<ProjectListDataData> _listDatas = new List();//内容集合
@override
void initState() {
super.initState();
getHttp();
}
void getHttp() async {
try {
var response = await HttpUtil().get(Api.PROJECT);
Map userMap = json.decode(response.toString());
var projectEntity = new ProjectEntity.fromJson(userMap);
setState(() {
if (_datas.length != null) _datas = projectEntity.data;
_currentIndex = 0;
});
getDetail();
//初始化controller并添加监听
controller = TabController(length: _datas.length, vsync: this);
controller.addListener(() => _onTabChanged());
} catch (e) {
print(e);
}
}
///
/// tab改变监听
///
_onTabChanged() {
if (_controller.index.toDouble() == _controller.animation.value) {
//赋值 并更新数据
this.setState(() {
_currentIndex = _controller.index;
});
getDetail();
}
}
///
/// 根据tab下标获取数据
///
void getDetail() async {
try {
var data = {"cid": _datas[_currentIndex].id};
var response = await HttpUtil().get(Api.PROJECT_LIST, data: data);
Map userMap = json.decode(response.toString());
var projectListEntity = new ProjectListEntity.fromJson(userMap);
setState(() {
_listDatas = projectListEntity.data.datas;
});
} catch (e) {
print(e);
}
}
@override
Widget build(BuildContext context) {
return new DefaultTabController(
length: _datas.length,
child: new Scaffold(
appBar: new TabBar(
controller: controller,//控制器
labelColor: YColors.colorPrimaryDark, //选中的颜色
labelStyle: TextStyle(fontSize: 16), //选中的样式
unselectedLabelColor: YColors.color_666, //未选中的颜色
unselectedLabelStyle: TextStyle(fontSize: 14), //未选中的样式
indicatorColor: YColors.colorPrimary, //下划线颜色
isScrollable: true, //是否可滑动
//tab标签
tabs: _datas.map((ProjectData choice) {
return new Tab(
text: choice.name,
);
}).toList(),
//点击事件
onTap: (int i) {
print(i);
},
),
body: new TabBarView(
controller: controller,
children: _datas.map((ProjectData choice) {
return new ListView.builder(
itemCount: _listDatas.length,
itemBuilder: (BuildContext context, int position) {
return getRow(position);
});
}).toList(),
),
),
);
}
Widget getRow(int i) {
return new GestureDetector(
child: new Container(
alignment: Alignment.topLeft,
padding: EdgeInsets.all(10),
child: new Card(
elevation: 5,
shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))),
color: Colors.white,
child: Padding(
padding: EdgeInsets.all(10),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Expanded(
flex: 2,
child: new Image.network(_listDatas[i].envelopePic),
),
new Expanded(
flex: 5,
child: new Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
new Padding(
padding: EdgeInsets.symmetric(horizontal: 10),
child: new Text(
_listDatas[i].title,
style: TextStyle(
fontSize: 16, fontWeight: FontWeight.bold),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
new Padding(
padding: EdgeInsets.all(10),
child: new Text(
_listDatas[i].desc,
style:
TextStyle(fontSize: 14, color: YColors.color_666),
maxLines: 3,
overflow: TextOverflow.ellipsis,
),
),
new Container(
alignment: Alignment.bottomLeft,
child: Row(
children: <Widget>[
new Expanded(
flex: 1,
child: new Padding(
padding: EdgeInsets.all(10),
child: new Text(_listDatas[i].niceDate,
style: TextStyle(fontSize: 14)),
),
),
new Padding(
padding: EdgeInsets.all(10),
child: new Text(
_listDatas[i].author,
style: TextStyle(fontSize: 14),
textAlign: TextAlign.right,
),
)
],
),
),
],
),
),
],
),
),
),
),
onTap: () {
//点击item跳转到详情
Navigator.push(
context,
new MaterialPageRoute(
builder: (context) => new ArticleDetail(
title: _listDatas[i].title, url: _listDatas[i].link)),
);
},
);
}
@override
void dispose() {
super.dispose();
controller.dispose();
}
}
github:https://github.com/yechaoa/wanandroid_flutter/blob/master/lib/pages/projectPage.dart
官方文档:https://api.flutter.dev/flutter/material/TabBar-class.html
推荐阅读
-
Flutter permission_handler 权限插件的使用详解
-
Flutter使用JsBridge方式处理Webview与H5通信的方法
-
使用Flutter实现一个走马灯布局的示例代码
-
Flutter中如何使用WillPopScope的示例代码
-
Flutter学习笔记(17)--顶部导航TabBar、TabBarView、DefaultTabController
-
关于flutter插件地图的使用flutter_map
-
Flutter中mixin的使用详解
-
使用Flutter开发的抖音国际版实例代码详解
-
Flutter 使用插件permission_handler的一些问题
-
flutter中的状态管理Provider 简单使用