当前位置: 首页 > news >正文

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,);}
}

可以按需求封装下就能上手使用了

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 鸿蒙开发:Universal Keystore Kit(密钥管理服务)【查询密钥是否存在(ArkTS)】
  • 东软医疗 踩在中国医疗科技跃迁的风口上
  • 【unity实战】使用unity制作一个红点系统
  • 文件安全传输系统,如何保障信创环境下数据的安全传输?
  • docker 安装 onlyoffice
  • CentOS 7 中出现 cannot open Packages database in /var/lib/rpm 错误
  • 最新PHP自助商城源码,彩虹商城源码
  • kettle从入门到精通 第七五课 ETL之kettle血缘,数据血缘
  • 【笔记】先求修改没保存的文本文件-在虚拟机中输入 yum makecache报错
  • 【Diffusion学习】【生成式AI】Diffusion Model 原理剖析 (2/4) (optional)【公式推导】
  • 微信小程序开发基础知识6----使用npm包
  • 探索Mojo编程语言:AI开发者的新宠儿
  • STM32学习和实践笔记(40):DS18B20温度传感器实验
  • 有关电力电子技术的一些相关仿真和分析:⑤交-直-交全桥逆变+全波整流结构电路(MATLAB/Siumlink仿真)
  • 1.3- Zygote
  • 【162天】黑马程序员27天视频学习笔记【Day02-上】
  • 【MySQL经典案例分析】 Waiting for table metadata lock
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • DOM的那些事
  • JSONP原理
  • JS学习笔记——闭包
  • python_bomb----数据类型总结
  • React Native移动开发实战-3-实现页面间的数据传递
  • 阿里云应用高可用服务公测发布
  • 初识 webpack
  • 从PHP迁移至Golang - 基础篇
  • 大快搜索数据爬虫技术实例安装教学篇
  • 聊聊flink的BlobWriter
  • 异常机制详解
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​探讨元宇宙和VR虚拟现实之间的区别​
  • #etcd#安装时出错
  • #if和#ifdef区别
  • (+4)2.2UML建模图
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (1) caustics\
  • (2015)JS ES6 必知的十个 特性
  • (C++二叉树05) 合并二叉树 二叉搜索树中的搜索 验证二叉搜索树
  • (delphi11最新学习资料) Object Pascal 学习笔记---第8章第5节(封闭类和Final方法)
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (二十三)Flask之高频面试点
  • (附源码)ssm码农论坛 毕业设计 231126
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (十五)使用Nexus创建Maven私服
  • (四)stm32之通信协议
  • (四)图像的%2线性拉伸
  • (幽默漫画)有个程序员老公,是怎样的体验?
  • (转)linux 命令大全
  • ***汇编语言 实验16 编写包含多个功能子程序的中断例程
  • .Family_物联网
  • .net 7和core版 SignalR
  • .NET 依赖注入和配置系统
  • .NET设计模式(8):适配器模式(Adapter Pattern)
  • .NET项目中存在多个web.config文件时的加载顺序
  • .NET正则基础之——正则委托