我正在参与「启航计划」
Tab选项卡,这是一个十分常见且权重很高的一个组件,随便打开一个App,比方,如下图,首页顶部便是一个Tab选项卡,这个功用能够说,几乎每个App都会存在。
在Android中,咱们能够运用TabLayout+ViewPager,轻松的完成一个Tab指示器+页面滑动,而在Flutter傍边呢,能够很负责任的告诉咱们,也是很简略的就能够完成,主要运用到了TabBar和TabBarView,举一个特别简略的比方,如下代码所示,便是十分简略的Tab选项卡+底部页面的作用。
@override
Widget build(BuildContext context) {
List<Widget> tabs = []; //tab指示器
List<Widget> bodyList = []; //tab指示器下面的内容Widget
for (int i = 0; i < 9; i++) {
tabs.add(Tab(text: "条目$i"));
bodyList.add(Text("条目$i"));//内容可所以恣意的Widget,比方列表等
}
return DefaultTabController(
// 标签数量
length: tabs.length,
child: Scaffold(
appBar: TabBar(
// 多个标签时翻滚加载
isScrollable: true,
// 标签指示器的色彩
indicatorColor: Colors.red,
// 标签的色彩
labelColor: Colors.red,
// 未选中标签的色彩
unselectedLabelColor: Colors.black,
// 指示器的巨细
indicatorSize: TabBarIndicatorSize.label,
// 指示器的权重,即线条高度
indicatorWeight: 4.0,
tabs: tabs),
// 标签页所对应的页面
body: TabBarView(children: bodyList)));
}
代码作用如下:
在Flutter傍边完成起来是不是也是十分的简略呢,既然现已如此的简略了,为什么咱们还要再封装一层呢?说白了一是为了扩展,扩展一下体系无法满意的功用,二是为了调用起来得心应手。ok,废话不多说,开始今天的概述。
今天的内容大概如下:
1、封装作用一览
2、确定封装特点和拓宽特点
3、源码和详细运用
4、相关总结
一、封装作用一览
一切的作用都是依据原生而完成的,如下图所示:
二、确定封装特点和拓宽特点
基本上封装的作用就如上图所示,要封装哪些特点,关于体系的特点,比方指示器的色彩,标签选中和未选中的色彩等等,都能够抛出去,让运用者选择性进行运用。
而需要的拓宽的特点,就使得自定义的Tab愈加的灵敏,满意不同的实践的需求,比方,文本指示器,图片指示器,图文指示器等等,都能够灵敏的添加一下。
详细的特点如下,咱们在实践封装中,能够依据本身需要来动态的灵敏的设置。
特点 | 类型 | 概述 |
---|---|---|
tabTitleList | List<String> | tab指示器的标题调集,文字方式 |
tabImageList | List<String> | tab指示器的标题调集,图片方式 |
tabWidgetList | List<Widget> | tab指示器的标题调集,Widget方式 |
tabIconAndTextList | List<TabBarBean> | tab指示器的标题调集,左图右文方式 |
tabBodyList | List<Widget> | tab指示器对应的页面 |
onPageChange | Function(int) | 页面滑动回调 |
indicatorColor | Color | 指示器的色彩 |
labelColor | Color | 标签的色彩 |
unselectedLabelColor | Color | 未选中标签的色彩 |
indicatorSize | TabBarIndicatorSize | 指示器的巨细是和文字宽度相同仍是充溢 |
indicatorHeight | double | indicatorHeight |
isScrollable | bool | 指示器是否支撑滑动 |
tabImageWidth | double | 图片指示器的宽仅用于图片指示器和图文指示器 |
tabImageHeight | double | 图片指示器的高仅用于图片指示器和图文指示器 |
tabIconAndTextMargin | double | 左图右文指示器,icon间隔文字的间隔 |
tabHeight | double | tab高度 |
三、源码和详细运用
源码相对比较的简略,仅仅对TabBar和TabBarView做了简略的封装,支撑了多种格式的Tab类型,因为需要Tab控制器,这儿运用了有状况的StatefulWidget。源码全体如下:
import 'package:flutter/material.dart';
import 'package:vip_flutter/ui/widget/vip_text.dart';
///AUTHOR:AbnerMing
///DATE:2023/5/18
///INTRODUCE:TabBar组件
class VipTabBarView extends StatefulWidget {
final List<String>? tabTitleList; //tab指示器的标题调集,文字方式
final List<String>? tabImageList; //tab指示器的标题调集,图片方式
final List<Widget>? tabWidgetList; //tab指示器的标题调集,Widget方式
final List<VipTabBarBean>? tabIconAndTextList; //tab指示器的标题调集,左图右文方式
final List<Widget>? tabBodyList; //tab指示器的页面
final Function(int)? onPageChange; //页面滑动回调
final Color? indicatorColor; //指示器的色彩
final Color? labelColor; //标签的色彩
final Color? unselectedLabelColor; //未选中标签的色彩
final TabBarIndicatorSize? indicatorSize; //指示器的巨细 是和文字宽度相同仍是充溢
final double? indicatorHeight; //指示器的高度
final bool? isScrollable; //指示器是否支撑滑动
final double? tabImageWidth; //图片指示器的宽 仅用于图片指示器和图文指示器
final double? tabImageHeight; //图片指示器的高 仅用于图片指示器和图文指示器
final double? tabIconAndTextMargin; //左图右文指示器,icon间隔文字的间隔
final double? tabHeight; //tab高度
const VipTabBarView(
{this.tabTitleList,
this.tabImageList,
this.tabWidgetList,
this.tabIconAndTextList,
this.tabBodyList,
this.onPageChange,
this.indicatorColor = Colors.black,
this.labelColor = Colors.black,
this.unselectedLabelColor = Colors.grey,
this.indicatorSize = TabBarIndicatorSize.tab,
this.indicatorHeight = 2,
this.isScrollable = true,
this.tabImageWidth = 15,
this.tabImageHeight = 15,
this.tabIconAndTextMargin = 5,
this.tabHeight = 44,
super.key});
@override
State<VipTabBarView> createState() => _GWMTabBarViewState();
}
///左图右文的对象
class VipTabBarBean {
String title;
String icon;
VipTabBarBean(this.title, this.icon);
}
class _GWMTabBarViewState extends State<VipTabBarView>
with SingleTickerProviderStateMixin {
// 标签控制器
late TabController _tabController;
@override
void initState() {
super.initState();
// 定义控制器
_tabController = TabController(
vsync: this,
length: widget.tabBodyList != null ? widget.tabBodyList!.length : 0,
);
// 添加监听事件
_tabController.addListener(() {
//滑动的索引
if (widget.onPageChange != null && !_tabController.indexIsChanging) {
widget.onPageChange!(_tabController.index);
}
});
}
@override
void dispose() {
super.dispose();
// 杀死控制器
_tabController.dispose();
}
/*
* 指示器点击
*/
void onPage(position) {}
@override
Widget build(BuildContext context) {
List<Widget> tabList = []; //tab指示器
List<Widget> bodyList = []; //tab指示器对应的页面
//文字方式
if (widget.tabTitleList != null) {
tabList = widget.tabTitleList!
.map((e) => Tab(
text: e,
height: widget.tabHeight,
))
.toList();
}
//图片方式
if (widget.tabImageList != null) {
tabList = widget.tabImageList!.map((e) {
Widget view;
if (e.contains("http")) {
//网络图片
view = Image.network(
e,
width: widget.tabImageWidth,
height: widget.tabImageHeight,
);
} else {
view = Image.asset(
e,
width: widget.tabImageWidth,
height: widget.tabImageHeight,
);
}
return Tab(icon: view, height: widget.tabHeight);
}).toList();
}
//自定义Widget
if (widget.tabWidgetList != null) {
tabList = widget.tabWidgetList!;
}
//左图右文方式
if (widget.tabIconAndTextList != null) {
tabList = widget.tabIconAndTextList!.map((e) {
return VipText(
e.title,
leftIcon: e.icon,
height: widget.tabHeight,
leftIconWidth: widget.tabImageWidth,
leftIconHeight: widget.tabImageHeight,
iconMarginRight: widget.tabIconAndTextMargin,
);
}).toList();
}
//指示器对应的页面
if (widget.tabBodyList != null) {
bodyList = widget.tabBodyList!.map((e) => e).toList();
}
return Scaffold(
appBar: TabBar(
// 加上控制器
controller: _tabController,
tabs: tabList,
// 标签指示器的色彩
indicatorColor: widget.indicatorColor,
// 标签的色彩
labelColor: widget.labelColor,
// 未选中标签的色彩
unselectedLabelColor: widget.unselectedLabelColor,
// 指示器的巨细
indicatorSize: widget.indicatorSize,
// 指示器的权重,即线条高度
indicatorWeight: widget.indicatorHeight!,
// 多个标签时翻滚加载
isScrollable: widget.isScrollable!,
onTap: onPage,
),
body: TabBarView(
// 加上控制器
controller: _tabController,
children: bodyList,
),
);
}
}
简略运用
传一个标题调集和页面调集就能够轻松完成了。
@override
Widget build(BuildContext context) {
return const VipTabBarView(
tabTitleList: ["条目一", "条目二"],
tabBodyList: [
Text("第一个页面"),//可所以恣意的Widget
Text("第二个页面"),//可所以恣意的Widget
],
);
}
一切案例
对应第一条的封装作用,可直接仿制查看作用。
import 'package:flutter/material.dart';
import '../widget/vip_tab_bar_view.dart';
import '../widget/vip_text.dart';
///AUTHOR:AbnerMing
///DATE:2023/5/20
///INTRODUCE:TabBar组件作用页面
class TabBarPage extends StatefulWidget {
const TabBarPage({super.key});
@override
State<TabBarPage> createState() => _TabBarPageState();
}
class _TabBarPageState extends State<TabBarPage> {
@override
Widget build(BuildContext context) {
var tabs = ["条目一", "条目二", "条目三", "条目四", "条目五", "条目六", "条目七", "条目八"];
var tabs2 = ["条目一", "条目二", "条目三"];
var tabImages = [
"https://www.6hu.cc/wp-content/uploads/2023/05/1684856137-ae6cc97a5bd331d.png",
"",
"https://www.6hu.cc/wp-content/uploads/2023/05/1684856146-bbd8f7c602ebd0d.png"
]; //图片指示器
var bodyList = tabs
.map((e) => VipText(e, backgroundColor: Colors.amberAccent))
.toList();
var bodyList2 = tabs2
.map((e) => VipText(e, backgroundColor: Colors.amberAccent))
.toList();
return Column(children: [
const VipText("多个Tab滑动",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabTitleList: tabs,
tabBodyList: bodyList,
onPageChange: ((position) {
//页面滑动监听
print(position);
}),
)),
const VipText("固定Tab不滑动",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabTitleList: tabs2,
tabBodyList: bodyList2,
isScrollable: false,
)),
const VipText("修正指示器色彩",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabTitleList: tabs2,
tabBodyList: bodyList2,
isScrollable: false,
labelColor: Colors.red,
unselectedLabelColor: Colors.black,
indicatorColor: Colors.red,
)),
const VipText("修正指示器巨细",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabTitleList: tabs2,
tabBodyList: bodyList2,
isScrollable: false,
labelColor: Colors.red,
unselectedLabelColor: Colors.black,
indicatorColor: Colors.red,
indicatorSize: TabBarIndicatorSize.label,
)),
const VipText("图片指示器",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabImageList: tabImages,
tabBodyList: bodyList2,
isScrollable: false,
labelColor: Colors.red,
unselectedLabelColor: Colors.black,
indicatorColor: Colors.red,
indicatorSize: TabBarIndicatorSize.label,
)),
const VipText("左图右文指示器",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabIconAndTextList: [
VipTabBarBean(
"Java", "https://www.6hu.cc/wp-content/uploads/2023/05/1684856137-ae6cc97a5bd331d.png"),
VipTabBarBean("Android",
""),
VipTabBarBean("Kotlin",
"https://www.6hu.cc/wp-content/uploads/2023/05/1684856146-bbd8f7c602ebd0d.png"),
],
tabBodyList: bodyList2,
isScrollable: false,
labelColor: Colors.red,
unselectedLabelColor: Colors.black,
indicatorColor: Colors.red,
indicatorSize: TabBarIndicatorSize.label,
))
]);
}
}
四、相关总结
在Flutter中咱们运用Tab选项卡和底部的页面结合运用时,一定要考虑懒加载,不然,在有网络恳求的时分,每次切换页面的时分,数据都会重新加载,这给用户体验是相当的欠好,详细如何完成,咱们能够去网上查找查找,有大把的文章概述,这儿就不赘述了,好了铁子们,本篇文章就先到这儿,期望能够帮助到咱们。