赞
踩
Tab选项卡,这是一个非常常见且权重很高的一个组件,随便打开一个App,比如CSDN,如下图,首页顶部就是一个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.vipandroid.cn/ming/pic/new_java.png",
- "https://www.vipandroid.cn/ming/pic/new_android.png",
- "https://www.vipandroid.cn/ming/pic/new_kotlin.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.vipandroid.cn/ming/pic/new_java.png"),
- VipTabBarBean("Android",
- "https://www.vipandroid.cn/ming/pic/new_android.png"),
- VipTabBarBean("Kotlin",
- "https://www.vipandroid.cn/ming/pic/new_kotlin.png"),
- ],
- tabBodyList: bodyList2,
- isScrollable: false,
- labelColor: Colors.red,
- unselectedLabelColor: Colors.black,
- indicatorColor: Colors.red,
- indicatorSize: TabBarIndicatorSize.label,
- ))
- ]);
- }
- }
在Flutter中我们使用Tab选项卡和底部的页面结合使用时,一定要考虑懒加载,否则,在有网络请求的时候,每次切换页面的时候,数据都会重新加载,这给用户体验是相当的不好,具体如何实现,大家可以去网上搜索搜索,有大把的文章概述,这里就不赘述了,好了铁子们,本篇文章就先到这里,希望可以帮助到大家。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。