flutter 手写 TabBar
前言:
这几天在使用 flutter TabBar 的时候 我们的设计给我提了一个需求:
如下 Tabbar 第一个元素 左对齐,试了下TabBar 的配置,无法实现这个需求,他的 配置是针对所有元素的。而且 这个 TabBar 下面的 滑块在移动的时候 上面的文字会相应的抖动。
看了下 TabBar 的源代码 他的实现是相对复杂的 下面的 滑块是 canvas 实现的。 有可能他要实现的功能比较丰富。
自定义Tabbar 的基本布局
下面是我页面的布局:这样实现起来 里面元素的 样式可以完全自己定义单个配置,想怎么显示都可以。这样就可以不用局限于 自带Tabbar的配置
SingleChildScrollView 解析
完成页面布局相对简单,主要实现底部 滑块的移动,以及 整 SingleChildScrollView 的居中移动是一个关键点:
ScrollController 中的几个关键概念:
- controller.position.viewportDimension: SingleChildScrollView 视口 的大小
- position.maxScrollExtent : SingleChildScrollView 可以移动的最大范围
- position.minScrollExtent : SingleChildScrollView 可以移动最小范围 一般是0
- Row 的长度就是所有元素的长度之和:也就是 position.maxScrollExtent + position.viewportDimension
Row 的长度之和 为什么是 SingleChildScrollView 最大可移动范围加 position.viewportDimension 的和?
SingleChildScrollView 可见区域始终是他的视口大小,不可见的也就是 Row的长度减去视口大小 也就是 maxScrollExtent 可拖动的最大区域,
实现 Tabbar
下面是我实现的大致效果:第一个元素左对齐,最后一个元素右对齐,我这边是直接写死的,你们封装一下 在外边直接用就好了。
代码如下:
import 'dart:ui';import 'package:flutter/material.dart'; import 'package:game/const/app_textStyle.dart'; import 'package:game/utils/app_widget.dart'; import 'package:game/wrap/extension/extension.dart';class PageTabBar extends StatefulWidget {const PageTabBar({Key? key}) : super(key: key);@overrideState<PageTabBar> createState() => _PageTabBarState(); }class _PageTabBarState extends State<PageTabBar> {final ScrollController _controller = ScrollController();int _selectIndex = 0;double _width = 0;double _positionX = 0;final List<Map> _listMap = [{'width': 0, 'name': '一号', 'key': GlobalKey()},{'width': 0, 'name': '二二号技师', 'key': GlobalKey()},{'width': 0, 'name': '三三三号技师', 'key': GlobalKey()},{'width': 0, 'name': '四号技师', 'key': GlobalKey()},{'width': 0, 'name': '五五号技师', 'key': GlobalKey()},{'width': 0, 'name': '六六六号技师', 'key': GlobalKey()},{'width': 0, 'name': '七号技师', 'key': GlobalKey()},{'width': 0, 'name': '八八号技师', 'key': GlobalKey()},{'width': 0, 'name': '九', 'key': GlobalKey()},{'width': 0, 'name': '十号技师', 'key': GlobalKey()},];@overridevoid initState() {// TODO: implement initStatesuper.initState();WidgetsBinding.instance.addPostFrameCallback((_) => _printSize());// _controller.addListener(() {// print('_controller.offset:${_controller.offset}');// });}@overridevoid dispose() {// TODO: implement dispose_controller.dispose();super.dispose();}void _printSize() {for (Map element in _listMap) {final RenderBox box = element['key'].currentContext.findRenderObject();element['width'] = box.size.width;}_width = _listMap[0]['width'];_selectItem(0);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppWidget.appBar(title: 'TabBar 测试页面'),body: Center(child: Container(height: 220.cale,width: 710.cale,color: Colors.deepOrangeAccent,child: SingleChildScrollView(physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics(),),controller: _controller,scrollDirection: Axis.horizontal,child: Stack(children: [Row(children: _listMap.asMap().map((key, value) => MapEntry(key,AppWidget.inkWellEffectNone(onTap: () {_selectItem(key);},child: Container(padding: key == 0? EdgeInsets.only(right: 25.cale): key == _listMap.length - 1? EdgeInsets.only(left: 25.cale): EdgeInsets.symmetric(horizontal: 25.cale),key: value['key'],color: Colors.blue.withOpacity(0.1 * key),height: 180.cale,child: Center(child: Text(value['name'],style: _selectIndex == key? AppTextStyle.textStyle_34_FD3949_Bold: AppTextStyle.textStyle_30_1A1A1A,),),),),),).values.toList(),),AnimatedPositioned(bottom: 0.cale,left: _positionX,duration: const Duration(milliseconds: 250),child: AnimatedContainer(duration: const Duration(milliseconds: 250),width: _width,child: Container(height: 20.cale,margin: EdgeInsets.symmetric(horizontal: 25.cale),width: double.infinity,color: Colors.green,),),)],),),),),);}void _selectItem(int index) {print('index:$index');final ScrollPosition position = _controller.position;setState(() {_selectIndex = index;_width = _listMap[index]['width'];});_positionX = 0;List.generate(index, (itemIndex) {_positionX += _listMap[itemIndex]['width'];});//当前展示的元素位置 中心点位置,用户确定 滚动位置double viewPosition = _positionX + _listMap[index]['width'] / 2;double movePosition = viewPosition - position.viewportDimension / 2;movePosition = clampDouble(movePosition, position.minScrollExtent, position.maxScrollExtent);_controller.animateTo(movePosition,duration: const Duration(milliseconds: 300),curve: Curves.easeOut,);} }可以按需求封装下就能上手使用了
相关文章:
flutter 手写 TabBar
前言: 这几天在使用 flutter TabBar 的时候 我们的设计给我提了一个需求: 如下 Tabbar 第一个元素 左对齐,试了下TabBar 的配置,无法实现这个需求,他的 配置是针对所有元素的。而且 这个 TabBar 下面的 滑块在移动的时…...
一个引发openssl崩溃问题案例
1 背景 最近用libevent写了一个https代理功能,在调研的时候,遇到了一个项目用到了本地多个openssl库引发的ssl握手崩溃问题。 2 开发环境 项目库版本号依赖项libeventlibevent-2.1.8-stableopenssl 1.1openssl1.0u / 1.1.1w / 3.3.1...... 3 问题现象…...
如何申请抖音本地生活服务商?3种方式优劣势分析!
随着多家互联网大厂在本地生活板块的布局力度不断加大,以抖音为代表的头部互联网平台的本地生活服务商成为了创业赛道中的大热门,与抖音本地生活服务商怎么申请等相关的帖子,更是多次登顶创业者社群的话题榜单。 就目前的市场情况来看&#x…...
【项目-轻量级Web Server lock类】
信号同步机制封装 Lock类信号量——sem类初始化信号量sem_init()销毁信号量sem_destory()对信号量进行P操作sem_wait()对信号进行V操作sem_post() 互斥锁——locker类初始化互斥量pthread_mutex_init()销毁互斥量pthread_mutex_destroy()给互斥锁加锁pthread_mutex_lock()解锁互…...
数据分析_计划
我做大数据的有6年了,以前都是用sql,或者spark,java,scala,python去做。现在这些平台搭建、维护、大多数都是搭建一次就完了,而且维护大多是大厂直接用云平台去做。ETL也是就做一次就够了,我们公…...
LDAPWordlistHarvester:基于LDAP数据的字典生成工具
关于LDAPWordlistHarvester LDAPWordlistHarvester是一款功能强大的字典列表生成工具,该工具可以根据LDAP中的详细信息生成字典列表文件,广大研究人员随后可以利用生成的字典文件测试目标域账号的非随机密码安全性。 工具特征 1、支持根据LDAP中的详细信…...
dhtmlx-gantt甘特图数据展示
官网文档:甘特图文档 实现效果: 首先需要下载 dhtmlx-gantt组件 npm i dhtmlx-gantt //我项目中使用的是"dhtmlx-gantt": "^8.0.6" 这个版本,不同的版本api或是文档中存在的方法稍有差异 界面引用 <template>&l…...
《云原生安全攻防》-- 容器攻击案例:Docker容器逃逸
当攻击者获得一个容器环境的shell权限时,攻击者往往会尝试进行容器逃逸,利用容器环境中的错误配置或是漏洞问题,从容器成功逃逸到宿主机,从而获取到更高的访问权限。 在本节课程中,我们将详细介绍一些常见的容器逃逸方…...
初学者指南:如何搭建和配置 Nginx 服务器
初学者指南:如何搭建和配置 Nginx 服务器 Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 代理服务器。本文将详细介绍如何在 Linux 上安装、配置和管理 Nginx 服务器。 一、安装 Nginx Nginx 可以安装在多种操作系统上&#x…...
[AHK] WinHttpRequest.5.1报错 0x80092004 找不到对象或属性
目录 背景描述 用浏览器访问,正常返回 编辑 AHK v2官方示例源代码 AHK v2运行结果报错(0x80092004) 找不到对象或属性 用thqby大佬的WinHttpRequest.ahk库测试报错 0x80092004 找不到对象或属性 附: 用Apifox访问,也正常返回 AHK v1 …...
`speech_recognition` 是一个流行的库
在Python中,speech_recognition 是一个流行的库,用于从各种来源(如麦克风、文件等)进行语音识别。以下是您提到的技术名称和相应的Python代码示例。 技术名称 语音识别(Speech Recognition):这…...
MFC Ribbon菜单 - 中英文实时切换方法
简介 最近在搞一个老外的项目,本来谈的好好的,纯英文界面。项目接近尾声了,又提出了中英文实时切换的新需求,没办法就只能想办法,毕竟客户最大嘛。 实现方法 还好本来的ribbon英文菜单不复杂,就用纯C编码…...
MFC程序创建word,创建表格,写入数据
文章目录 1、MFC程序功能:2、MFC程序实现2.1 创建项目2.2 添加word操作类2.3 添加word资源2.4 编写代码,实现将数据写入到word2.5 运行程序、验证功能3、工程代码下载 1、MFC程序功能: 创建word文档;向文档中写入字符串ÿ…...
FPGA:基于复旦微FMQL10S400 /FMQL20S400 国产化核心板
复旦微电子是国内集成电路设计行业的领军企业之一,早在2000年就在香港创业板上市,成为行业内首家上市公司。公司的RFID芯片、智能卡芯片、EEPROM、智能电表MCU等多种产品在市场上的占有率位居行业前列。 今天介绍的是搭载复旦微 FMQL10S400/FMQL20S400的…...
centos下使用yum安装keepalived工具
1、安装如下rpm包,不安装此包在装keepalived时会报错 mkdir keepalived_rpm cd keepalived_rpm/ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-community-libs-compat-8.0.36-1.el7.x86_64.rpm 2、使用yum进行keepalived的安装 yum -y install keepal…...
无人机图像目标检测
本仓库是人工智能课程的课程作业仓库,主要是完成无人机图像目标检测的任务,我们对visdrone数据集进行了处理,在yolo和ssd两种框架下进行了训练和测试,并编写demo用于实时的无人机图像目标检测。 requirements依赖: ss…...
JSqlParser 解析 sql
目录 前言一、Maven依赖二、获取sql中的表名三、获取sql中的具体信息 前言 JSqlParser是一个 SQL 语句解析器。它将 SQL 语句转换为可遍历的 Java 类层次结构,可以方便的用代码对 SQL 语句进行解析,修改等操作。 官网 api 文档和 github 地址如下&…...
Vue中使用mind-map实现在线思维导图
概述 在前面的文章Vue中实现在线画流程图实现中介绍了流程图的在线绘制,在本文,给大家分享一下基于mind-map实现在线的思维导图,并实现:1. 导图导出为图片;2. 打开xmind文件。 实现效果 实现 1. mind-map简介 simp…...
ChatGPT 深度解析:技术驱动的智能对话
在当今科技飞速发展的时代,ChatGPT 无疑成为了最耀眼的明星之一。它以其令人惊叹的智能对话能力,引发了全球范围内的广泛关注和热议。 ChatGPT 背后的技术堪称精妙绝伦。它基于深度学习算法,通过对海量数据的学习和分析,从而能够理…...
Armv8-R内存模型详解
目录 1.内存模型的必要性 2.Armv8-R内存模型分类 2.1 Normal memory 2.2 Device Memory 2.2.1 Gathering 2.2.2 Reordering 2.2.3 Early Write Acknowledgement 3.小结 大家好,今天是悲伤的肌肉。 在调研区域控制器芯片时,发现了S32Z、Stellar …...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
【位运算】消失的两个数字(hard)
消失的两个数字(hard) 题⽬描述:解法(位运算):Java 算法代码:更简便代码 题⽬链接:⾯试题 17.19. 消失的两个数字 题⽬描述: 给定⼀个数组,包含从 1 到 N 所有…...
【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验
系列回顾: 在上一篇中,我们成功地为应用集成了数据库,并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了!但是,如果你仔细审视那些 API,会发现它们还很“粗糙”:有…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
爬虫基础学习day2
# 爬虫设计领域 工商:企查查、天眼查短视频:抖音、快手、西瓜 ---> 飞瓜电商:京东、淘宝、聚美优品、亚马逊 ---> 分析店铺经营决策标题、排名航空:抓取所有航空公司价格 ---> 去哪儿自媒体:采集自媒体数据进…...
【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...
【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...
多模态图像修复系统:基于深度学习的图片修复实现
多模态图像修复系统:基于深度学习的图片修复实现 1. 系统概述 本系统使用多模态大模型(Stable Diffusion Inpainting)实现图像修复功能,结合文本描述和图片输入,对指定区域进行内容修复。系统包含完整的数据处理、模型训练、推理部署流程。 import torch import numpy …...




