开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 31 天,点击查看活动详情
TabBarView
TabBarView 封装了PageView。
TabBarView({
Key? key,
required this.children, // tab 页
this.controller, // TabController
this.physics,
this.dragStartBehavior = DragStartBehavior.start,
})
TabController用于监听和控制TabBarView的页面切换,通常和TabBar联动。如果没有指定,则会在组件树中向上查找并使用最近的一个DefaultTabController。
TabBar
TabBar为TbaBarView的导航标题,类似于新闻头条的标题导航栏。
const TabBar({
Key? key,
required this.tabs, // 具体的 Tabs,需要我们创建
this.controller,
this.isScrollable = false, // 是否可以滑动
this.padding,
this.indicatorColor,// 指示器颜色,默认是高度为2的一条下划线
this.automaticIndicatorColorAdjustment = true,
this.indicatorWeight = 2.0,// 指示器高度
this.indicatorPadding = EdgeInsets.zero, //指示器padding
this.indicator, // 指示器
this.indicatorSize, // 指示器长度,有两个可选值,一个tab的长度,一个是label长度
this.labelColor,
this.labelStyle,
this.labelPadding,
this.unselectedLabelColor,
this.unselectedLabelStyle,
this.mouseCursor,
this.onTap,
...
})
TabBar也有一个TabController,如果需要和TabBarView联动,可以使它们共用同一个TanController即可。需要注意的是联动时TabBar和TabBarView的子组件数量需保持一致。此外需要创建的tab并通过tabs传递给TabBar,tab可以是任意的Widget,不过Material组件库中已经实现了Tab组件:
const Tab({
Key? key,
this.text, //文本
this.icon, // 图标
this.iconMargin = const EdgeInsets.only(bottom: 10.0),
this.height,
this.child, // 自定义 widget
})
需要注意的是text和child是互斥的,不能同时指定。
实例:
class TabViewRoute1 extends StatefulWidget {
@override
_TabViewRoute1State createState() => _TabViewRoute1State();
}
class _TabViewRoute1State extends State<TabViewRoute1>
with SingleTickerProviderStateMixin {
late TabController _tabController;
List tabs = ["新闻", "历史", "图片"];
@override
void initState() {
super.initState();
_tabController = TabController(length: tabs.length, vsync: this);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("App Name"),
bottom: TabBar(
controller: _tabController,
tabs: tabs.map((e) => Tab(text: e)).toList(),
),
),
body: TabBarView( //构建
controller: _tabController,
children: tabs.map((e) {
return KeepAliveWrapper(
child: Container(
alignment: Alignment.center,
child: Text(e, textScaleFactor: 5),
),
);
}).toList(),
),
);
}
@override
void dispose() {
// 释放资源
_tabController.dispose();
super.dispose();
}
}
滑动页面时,顶部的Tab也会跟着动,点击顶部Tab时页面也会跟着切换。为了实现TabBar和TabBarView的联动,显式创建一个TabController,TabController需要一个Ticker Provider(vsync参数),又混入了SingleTickerProviderStateMixin;由于TabController中会执行动画,持有一些资源,在页面销毁时需要释放资源(dispose),也就是上面的代码思路。实际开发中,通常会创建一个DefaultTabController作为它们共同的父级组件,这样在执行时就会从组件树向上查找,都会使用指定的这个DefaultTabController。
class TabViewRoute2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
List tabs = ["新闻", "历史", "图片"];
return DefaultTabController(
length: tabs.length,
child: Scaffold(
appBar: AppBar(
title: Text("App Name"),
bottom: TabBar(
tabs: tabs.map((e) => Tab(text: e)).toList(),
),
),
body: TabBarView( //构建
children: tabs.map((e) {
return KeepAliveWrapper(
child: Container(
alignment: Alignment.center,
child: Text(e, textScaleFactor: 5),
),
);
}).toList(),
),
),
);
}
}
这样还无需手动的管理Controller的生命周期,也不需要提供SingleTickerPrividerStateMixin,同时也没有其他的状态需要管理,也不需要额外的创建StatefulWidget。从这可以看出Scaffold的强大之处。