Flutter控件之Tab选项卡封装

news/2024/11/14 21:40:10/

Tab选项卡,这是一个非常常见且权重很高的一个组件,随便打开一个App,比如CSDN,如下图,首页顶部就是一个Tab选项卡,这个功能可以说,几乎每个App都会存在。

在Android中,我们可以使用TabLayout+ViewPager,轻松的实现一个Tab指示器+页面滑动,而在Flutter当中呢,可以很负责任的告诉大家,也是很简单的就可以实现,主要使用到了TabBar和TabBarView,举一个特别简单的例子,如下代码所示,就是非常简单的Tab选项卡+底部页面的效果。

@overrideWidget build(BuildContext context) {List<Widget> tabs = []; //tab指示器List<Widget> bodyList = []; //tab指示器下面的内容Widgetfor (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});@overrideState<VipTabBarView> createState() => _GWMTabBarViewState();
}///左图右文的对象
class VipTabBarBean {String title;String icon;VipTabBarBean(this.title, this.icon);
}class _GWMTabBarViewState extends State<VipTabBarView>with SingleTickerProviderStateMixin {// 标签控制器late TabController _tabController;@overridevoid 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);}});}@overridevoid dispose() {super.dispose();// 杀死控制器_tabController.dispose();}/** 指示器点击*/void onPage(position) {}@overrideWidget 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();}//自定义Widgetif (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,),);}
}

简单使用

传一个标题集合和页面集合就可以轻松实现了。

  @overrideWidget build(BuildContext context) {return const VipTabBarView(tabTitleList:  ["条目一", "条目二"],tabBodyList: [Text("第一个页面"),//可以是任意的WidgetText("第二个页面"),//可以是任意的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});@overrideState<TabBarPage> createState() => _TabBarPageState();
}class _TabBarPageState extends State<TabBarPage> {@overrideWidget 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选项卡和底部的页面结合使用时,一定要考虑懒加载,否则,在有网络请求的时候,每次切换页面的时候,数据都会重新加载,这给用户体验是相当的不好,具体如何实现,大家可以去网上搜索搜索,有大把的文章概述,这里就不赘述了,好了铁子们,本篇文章就先到这里,希望可以帮助到大家。


http://www.ppmy.cn/news/78354.html

相关文章

EduSoho 网校系统部署

目录 一、初始化环境二、安装 Nginx三、安装 MySQL四、安装 PHP五、上线 EduSoho六、验证FAQ 一、初始化环境 1、安装wget 如果系统已安装wget,请跳过此步骤 yum install wget2、关闭防火墙/Selinux systemctl stop firewalld.service systemctl disable firewalld.service…

华为ACL(基本的高级的基于时间的自反的都有)

第12章:ACL 随着网络的飞速发展,网络安全和网络服务质量QoS (Quality of Service)问题日益突出。访问控制列表 (ACL, Access Control List)是与其紧密相关的一个技术。ACL可以通过对网络中报文流的精确识别,与其他技术结合,达到控制网络访问行为、防止网络攻击和提高网络带…

助力工业物联网,工业大数据之ODS层构建:申明分区代码及测试【十】

文章目录 知识点13&#xff1a;ODS层构建&#xff1a;申明分区代码及测试知识点14&#xff1a;ODS层与DWD层区别知识点15&#xff1a;DWD层构建&#xff1a;需求分析知识点16&#xff1a;DWD层构建&#xff1a;建库实现测试知识点17&#xff1a;DWD层构建&#xff1a;建表实现测…

【2023】某python语言程序设计跟学第八周内容

目录 1.实例&#xff1a;体育竞技分析1.1.问题分析1.2.自顶向下和自底向上1.3.体育竞技分析 2.python程序设计思维2.1.计算思维与程序设计2.2.计算生态与python语言2.3.用户体验与软件产品2.4.基本的程序设计模式 3.Python第三方库安装3.1.看见更大的Python世界3.2.pip安装方式…

数字音频接口I2S-PDM-TDM-PCM

主要分类&#xff1a;模拟、数字&#xff08;I2S、PCM、PDM、TDM&#xff09; 模拟音频&#xff0c;就是功放输出的&#xff0c;驱动音箱和喇叭的音频。模拟麦克风采样回来的数据也是模拟音频。通常会有单端或差分两种信号。 数字音频&#xff0c;不能直接驱动喇叭&#xff0…

3、Ray-分布式任务调度和依赖管理

3、Ray-分布式任务调度和依赖管理 导航 1.简介和背景 2.Ray的基本概念和核心组件 3.分布式任务调度和依赖管理 4.对象存储和数据共享 5.Actor模型和并发编程 6.Ray的高级功能和扩展性 7.使用Ray构建分布式应用程序的案例研究 8.Ray社区和资源 9.核心框架介绍 10.

windows先的conda环境复制到linux环境

如果是迁移的环境一致&#xff1a;同是windows或同是linux直接用这个命令即可&#xff1a; conda create -n new_env_name --clone old_env_path 如果是window的环境迁移到linux这种跨环境就不能用上面的方法&#xff0c;网上这方面的资料也很多&#xff0c;记录一下我的…

Vue实现仿window画图

目录 一、演示功能 二、原理实现 (1)、绘制图形 (2)、绘制逻辑原理 (3)、动画原理 (4)、图形变换原理 三、残留bug 四、源代码 一、演示功能 实现了绘制线、直线、矩形、圆 绘制时候还可以选择绘制属性(虚实线、填充图形、颜色) 可以执行图形变换操作 演示 二、原…