Flutter控件之Tab选项卡封装

这篇具有很好参考价值的文章主要介绍了Flutter控件之Tab选项卡封装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

flutter tab选项卡,Flutter点石成金,flutter,FlutterTab指示器,Flutter TabBar,TabBarView

在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 tab选项卡,Flutter点石成金,flutter,FlutterTab指示器,Flutter TabBar,TabBarView

在Flutter当中实现起来是不是也是非常的简单呢,既然已经如此的简单了,为什么我们还要再封装一层呢?说白了一是为了扩展,扩展一下系统无法满足的功能,二是为了调用起来得心应手。ok,废话不多说,开始今天的概述。

今天的内容大概如下:

1、封装效果一览

2、确定封装属性和拓展属性

3、源码和具体使用

4、相关总结

一、封装效果一览

所有的效果都是基于原生而实现的,如下图所示:

flutter tab选项卡,Flutter点石成金,flutter,FlutterTab指示器,Flutter TabBar,TabBarView

二、确定封装属性和拓展属性

基本上封装的效果就如上图所示,要封装哪些属性,关于系统的属性,比如指示器的颜色,标签选中和未选中的颜色等等,都可以抛出去,让使用者选择性进行使用。

而需要的拓展的属性,就使得自定义的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选项卡和底部的页面结合使用时,一定要考虑懒加载,否则,在有网络请求的时候,每次切换页面的时候,数据都会重新加载,这给用户体验是相当的不好,具体如何实现,大家可以去网上搜索搜索,有大把的文章概述,这里就不赘述了,好了铁子们,本篇文章就先到这里,希望可以帮助到大家。文章来源地址https://www.toymoban.com/news/detail-754687.html

到了这里,关于Flutter控件之Tab选项卡封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt 容器控件之Tab Widget 使用详解

    功能 QTabWidget 的一种带标签页的窗口,在这种类型的窗口中可以存储多个子窗口,每个子窗口的显示可以通过对应的标签进行切换。   相关API // 构造函数 QTabWidget::QTabWidget(QWidget *parent = Q_NULLPTR); // 公共成员函数 /* 添加选项卡addTab()或者插入选项卡insertTab()函数相关的参数如

    2024年02月13日
    浏览(35)
  • HTML5+CSS3小实例:3D翻转Tab选项卡切换特效

    实例:3D翻转Tab选项卡切换特效 技术栈:HTML+CSS 效果: 源码: 【HTML】

    2024年02月04日
    浏览(48)
  • Tab切换以及倒计时组件封装

    功能 支持默认选中tab 子元素可以是文本或者图片 自定义tab的数量,并自适应展示 实现方式 用ul li标签遍历传入的tabs数组参数渲染 判断是否传入背景,未传则显示文字 绑定点击事件 特点 简单易用 可适配性 功能 常用于榜单或者活动结束倒计时、或者开始倒计时、从而提高

    2024年02月09日
    浏览(35)
  • react使用hook封装一个tab组件

    2024年02月09日
    浏览(37)
  • 【ArcGIS Pro二次开发】(3):UI管理_显示隐藏Tab、Group、Control等控件

    在ArcGIS Pro工作中,有时候会涉及到工具栏UI的管理,比如,打开模型构建器时,工具栏才会出现新的选项卡(Tab)【ModelBuilder】,工程未做更改,则【保存】按钮显示灰色不可用。 下面以一个小例子来学习一下。 1、新建一个项目,命名为【UIManager】,再添加4个ArcGIS Pro 按钮【

    2024年02月08日
    浏览(36)
  • Flutter中状态管理选项的比较:利弊探索

    Flutter 应用程序开发的一个关键方面是管理状态,这确保了整个应用程序的数据一致性和更新。然而,Flutter 提供了多种状态管理解决方案,每种解决方案都有自己的优缺点。在这篇博客中,我们将探讨 Flutter 中一些流行的状态管理选项,并讨论它们的优缺点。 这些状态管理

    2024年01月25日
    浏览(35)
  • Flutter的Align控件

    Align是Flutter中的一个控件,用于根据指定的对齐方式对子控件进行位置调整。Align 可以在水平和垂直方向上对齐子部件,并根据需要进行缩放。 Align控件可以将其子控件放置在父控件中的特定位置,并通过alignment属性来指定对齐方式。alignment属性接受一个Alignment对象,用于描

    2024年02月09日
    浏览(25)
  • Flutter控件之CircularProgressIndicator

    Flutter中的 CircularProgressIndicator 是一个圆形进度指示器,用于表示正在进行的任务的进度。它通常用于长时间运行的任务,例如文件下载、网络请求等。 CircularProgressIndicator 可以在圆周上旋转,以表示正在进行的任务的进度,同时可以根据需要设置颜色、尺寸和线宽等属性。

    2024年02月10日
    浏览(26)
  • Flutter 实现任意控件拖动

    使用flutter开发是需要控件能拖动,比如画板中的元素,或者工具条,搜索框,每个都单独去实现拖动还是比较麻烦的,将拖动功能封装成一个控件,需要的时候直接使用拖动控件作为父控件这样就方便很多了。 使用translate变换位置即可 这一步不是必须的,但是如果需要限制

    2024年02月15日
    浏览(34)
  • 【Flutter】Flutter Text 控件实现下划线、删除线、虚线、加粗、斜体

    在 Flutter 开发中,我们经常需要对 Text 控件进行各种样式的设置,包括但不限于下划线、删除线、虚线、加粗和斜体等。这些样式的设置可以帮助我们更好地展示文本内容,提升用户体验。本文将详细介绍如何在 Flutter 3.10.0 或更高版本中实现这些效果。阅读本文后,你将掌握

    2024年02月06日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包