Flutter控件之Tab选项卡封装

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

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



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

@overrideWidgetbuild(BuildContextcontext) {
List<Widget>tabs= []; //tab指示器List<Widget>bodyList= []; //tab指示器下面的内容Widgetfor (inti=0; i<9; i++) {
tabs.add(Tab(text: "条目$i"));
bodyList.add(Text("条目$i"));//内容可以是任意的Widget,比如列表等    }
returnDefaultTabController(
// 标签数量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组件classVipTabBarViewextendsStatefulWidget {
finalList<String>?tabTitleList; //tab指示器的标题集合,文字形式finalList<String>?tabImageList; //tab指示器的标题集合,图片形式finalList<Widget>?tabWidgetList; //tab指示器的标题集合,Widget形式finalList<VipTabBarBean>?tabIconAndTextList; //tab指示器的标题集合,左图右文形式finalList<Widget>?tabBodyList; //tab指示器的页面finalFunction(int)?onPageChange; //页面滑动回调finalColor?indicatorColor; //指示器的颜色finalColor?labelColor; //标签的颜色finalColor?unselectedLabelColor; //未选中标签的颜色finalTabBarIndicatorSize?indicatorSize; //指示器的大小 是和文字宽度一样还是充满finaldouble?indicatorHeight; //指示器的高度finalbool?isScrollable; //指示器是否支持滑动finaldouble?tabImageWidth; //图片指示器的宽 仅用于图片指示器和图文指示器finaldouble?tabImageHeight; //图片指示器的高 仅用于图片指示器和图文指示器finaldouble?tabIconAndTextMargin; //左图右文指示器,icon距离文字的距离finaldouble?tabHeight; //tab高度constVipTabBarView(
      {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();
}
///左图右文的对象classVipTabBarBean {
Stringtitle;
Stringicon;
VipTabBarBean(this.title, this.icon);
}
class_GWMTabBarViewStateextendsState<VipTabBarView>withSingleTickerProviderStateMixin {
// 标签控制器lateTabController_tabController;
@overridevoidinitState() {
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);
      }
    });
  }
@overridevoiddispose() {
super.dispose();
// 杀死控制器_tabController.dispose();
  }
/** 指示器点击*/voidonPage(position) {}
@overrideWidgetbuild(BuildContextcontext) {
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) {
Widgetview;
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,
          );
        }
returnTab(icon: view, height: widget.tabHeight);
      }).toList();
    }
//自定义Widgetif (widget.tabWidgetList!=null) {
tabList=widget.tabWidgetList!;
    }
//左图右文形式if (widget.tabIconAndTextList!=null) {
tabList=widget.tabIconAndTextList!.map((e) {
returnVipText(
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();
    }
returnScaffold(
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,
      ),
    );
  }
}


简单使用


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


@overrideWidgetbuild(BuildContextcontext) {
returnconstVipTabBarView(
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组件效果页面classTabBarPageextendsStatefulWidget {
constTabBarPage({super.key});
@overrideState<TabBarPage>createState() =>_TabBarPageState();
}
class_TabBarPageStateextendsState<TabBarPage> {
@overrideWidgetbuild(BuildContextcontext) {
vartabs= ["条目一", "条目二", "条目三", "条目四", "条目五", "条目六", "条目七", "条目八"];
vartabs2= ["条目一", "条目二", "条目三"];
vartabImages= [
"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"    ]; //图片指示器varbodyList=tabs        .map((e) =>VipText(e, backgroundColor: Colors.amberAccent))
        .toList();
varbodyList2=tabs2        .map((e) =>VipText(e, backgroundColor: Colors.amberAccent))
        .toList();
returnColumn(children: [
constVipText("多个Tab滑动",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabTitleList: tabs,
tabBodyList: bodyList,
onPageChange: ((position) {
//页面滑动监听print(position);
            }),
          )),
constVipText("固定Tab不滑动",
alignment: Alignment.topLeft,
marginTop: 10,
style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(
height: 80,
child: VipTabBarView(
tabTitleList: tabs2,
tabBodyList: bodyList2,
isScrollable: false,
          )),
constVipText("修改指示器颜色",
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,
          )),
constVipText("修改指示器大小",
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,
          )),
constVipText("图片指示器",
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,
          )),
constVipText("左图右文指示器",
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选项卡和底部的页面结合使用时,一定要考虑懒加载,否则,在有网络请求的时候,每次切换页面的时候,数据都会重新加载,这给用户体验是相当的不好,具体如何实现,大家可以去网上搜索搜索,有大把的文章概述,这里就不赘述了,好了铁子们,本篇文章就先到这里,希望可以帮助到大家。

相关文章
|
Android开发 iOS开发 容器
Flutter控件封装之轮播图Banner
Flutter中实现轮播图的方式有很多种,比如使用三方flutter_swiper,card_swiper等等,使用这些三方,可以很快很方便的实现一个轮播图展示,基本上也能满足我们日常的开发需求,如果说,想要一些定制化的操作,那么就不得不去更改源码或者自己自定义一个,自己定义的话,Flutter中提供了原生组件PageView,可以使用它很方便的来实现一个轮播图。
298 0
|
30天前
|
JSON Dart API
Flutter dio http 封装指南说明
本文介绍了如何实现一个通用、可重构的 Dio 基础类,包括单例访问、日志记录、常见操作封装以及请求、输出、报错拦截等功能。
Flutter dio http 封装指南说明
|
1月前
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
flutter 导航组件 AppBar (含顶部选项卡TabBar,抽屉菜单 drawer ,自定义导航图标)
23 1
|
1月前
|
移动开发 UED 容器
Flutter-自定义可展开文本控件
Flutter-自定义可展开文本控件
36 0
|
1月前
Flutter-自定义三角形评分控件
Flutter-自定义三角形评分控件
21 0
|
1月前
|
缓存
Flutter仿Boss-6.底部tab切换
Flutter仿Boss-6.底部tab切换
21 0
|
1月前
Flutter仿Boss-5.Lottie实现的Tab切换
本文介绍了如何在Flutter项目中使用Lottie库创建自定义动画的Tab栏项(LottieBottomBarItem),并展示了如何集成到带有PageView的主页底部导航栏中,以及相关的状态管理和逻辑控制。
20 0
|
2月前
|
移动开发 小程序 安全
基础入门-APP架构&小程序&H5+Vue语言&Web封装&原生开发&Flutter
基础入门-APP架构&小程序&H5+Vue语言&Web封装&原生开发&Flutter
|
3月前
|
设计模式 JavaScript 前端开发
flutter组件封装技巧
工厂函数不会自动调用,需要手动调用
39 3
|
3月前
Flutter StatefulWidget传递数据,多级控件传递数据
Flutter StatefulWidget传递数据,多级控件传递数据 在Flutter中,StatefulWidget可以通过构造函数将数据传递给其子控件,这种方式适用于一些简单的场景。但是,当存在多级嵌套控件时,将数据从祖先传递到后代可能会变得困难。在这种情况下,可以使用Flutter提供的InheritedWidget类来传递数据。