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

Flutter:自定义Tab切换,订单列表页tab,tab吸顶

1、自定义tab切换

在这里插入图片描述

view

<Widget>[// 好评<Widget>[TDImage(assetUrl: 'assets/img/order4.png',width: 36.w,height: 36.w,),SizedBox(width: 10.w,),TextWidget.body('好评',size: 24.sp,color: controller.tabIndex == 0 ? AppTheme.colorfff : AppTheme.color999,),].toRow(mainAxisAlignment: MainAxisAlignment.center).card(color: controller.tabIndex == 0 ? AppTheme.error : AppTheme.colorfff,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.w))).tight(width: 160.w,height: 68.w).onTap(() {controller.onTapOrderStatus(0);}),SizedBox(width: 20.w,),// 中评<Widget>[TDImage(assetUrl: 'assets/img/order5.png',width: 36.w,height: 36.w,),SizedBox(width: 10.w,),TextWidget.body('中评',size: 24.sp,color: controller.tabIndex == 1 ? AppTheme.colorfff : AppTheme.color999,),].toRow(mainAxisAlignment: MainAxisAlignment.center).card(color: controller.tabIndex == 1 ? AppTheme.error : AppTheme.colorfff,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.w))).tight(width: 160.w,height: 68.w).onTap(() {controller.onTapOrderStatus(1);}),SizedBox(width: 20.w,),// 差评<Widget>[TDImage(assetUrl: 'assets/img/order6.png',width: 36.w,height: 36.w,),SizedBox(width: 10.w,),TextWidget.body('差评',size: 24.sp,color: controller.tabIndex == 2 ? AppTheme.colorfff : AppTheme.color999,),].toRow(mainAxisAlignment: MainAxisAlignment.center).card(color: controller.tabIndex == 2 ? AppTheme.error : AppTheme.colorfff,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.w))).tight(width: 160.w,height: 68.w).onTap(() {controller.onTapOrderStatus(2);}),
].toRow(),

controller

import 'package:flutter/material.dart';
import 'package:get/get.dart';
class OrderEvaluateController extends GetxController{OrderEvaluateController();// tab 索引int tabIndex = 0;// 状态改变onTapOrderStatus(int index){tabIndex = index;// print('tabIndex: $tabIndex');update(["order_evaluate"]);}@overridevoid onClose() {super.onClose();tabController.dispose();}
}

2、订单列表tab,可以指定tabIndex的初始位置

在这里插入图片描述

view

import 'package:demo/common/index.dart';
import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'index.dart';class OrderListPage extends GetView<OrderListController> {const OrderListPage({super.key});// tab切换Widget _buildTab() {return TDTabBar(controller: controller.tabController,tabs: controller.tabNames.map((e) => TDTab(text: e)).toList(),onTap: (index) {controller.onTapOrderStatus(index);},backgroundColor: Colors.white,showIndicator: true,indicatorColor: const Color(0xffe01e1e),labelColor: const Color(0xffe01e1e),);}// list 列表Widget _buildList() {return SmartRefresher(controller: controller.refreshController,enablePullUp: true, // 启用上拉加载onRefresh: controller.onRefresh, // 下拉刷新回调onLoading: controller.onLoading, // 上拉加载回调footer: const SmartRefresherFooterWidget(), // 底部加载更多组件header: const SmartRefresherHeaderWidget(), // 顶部下拉刷新组件child: ListView.builder(itemCount: 10,itemBuilder: (context, index) {return _buildItem();},),);}// 商品itemWidget _buildItem() {return Text('item')}// 主视图Widget _buildView() {return <Widget>[_buildTab(),Expanded(child: _buildList()),].toColumn();}@overrideWidget build(BuildContext context) {return GetBuilder<OrderListController>(init: OrderListController(),id: "order_list",builder: (_) {return Scaffold(backgroundColor: const Color(0xffF5F6FA),appBar: const TDNavBar(height: 45,title: '我的订单',titleFontWeight: FontWeight.w600,backgroundColor: Colors.white,screenAdaptation: true,useDefaultBack: true,),body: SafeArea(child: _buildView(),),);},);}
}

controller

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';class OrderListController extends GetxController with GetSingleTickerProviderStateMixin{OrderListController();// 订单列表List items = [];// tab 控制器late TabController tabController;// tab 索引int tabIndex = Get.arguments["tabIndex"] ?? 0;// tab 名称List<String> tabNames = ['全部', '待付款', '待发货', '待收货', '已完成'];// 订单状态改变onTapOrderStatus(int index){tabIndex = index;// 刷新订单列表refreshController.requestRefresh();update(["order_list"]);}@overridevoid onInit() {super.onInit();// 初始化时设置当前索引tabController = TabController(length: tabNames.length,vsync: this,initialIndex: tabIndex, // 设置初始索引);// 监听 tab 切换tabController.addListener(() {if (tabController.indexIsChanging) {tabIndex = tabController.index;update(["order_list"]);}});}@overridevoid onClose() {super.onClose();refreshController.dispose();tabController.dispose();}/** 分页* refreshController:分页控制器* _page:分页* _limit:每页条数* _loadNewsSell:拉取数据(是否刷新)* onLoading:上拉加载新商品* onRefresh:下拉刷新* */final RefreshController refreshController = RefreshController(initialRefresh: true,);// int _page = 1;// int _limit = 20;Future<bool> _loadNewsSell(bool isRefresh) async {return false;// var result = await ProductApi.products(ProductsReq(//   page:isRefresh ? 1:_page,//     prePage:_limit// ));// if(isRefresh){//   _page = 1;//   items.clear();// }// if(result.isNotEmpty){//   _page++;//   items.addAll(result);// }// // 是否是空// return result.isEmpty;}// 上拉载入新商品void onLoading() async {if (items.isNotEmpty) {try {// 拉取数据是否为空 ? 设置暂无数据 : 加载完成var isEmpty = await _loadNewsSell(false);isEmpty? refreshController.loadNoData(): refreshController.loadComplete();} catch (e) {refreshController.loadFailed(); // 加载失败}} else {refreshController.loadNoData(); // 设置无数据}update(["goods_list"]);}// 下拉刷新void onRefresh() async {try {await _loadNewsSell(true);refreshController.refreshCompleted();} catch (e) {refreshController.refreshFailed();}update(["goods_list"]);}
}

3、tab吸顶,主要记录view中的实现。
下拉刷新和tab切换的方法基本与上方2、的controller一致

在这里插入图片描述
在这里插入图片描述

view

import 'package:ducafe_ui_core/ducafe_ui_core.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';
import 'package:tdesign_flutter/tdesign_flutter.dart';
import 'package:xiaoshukeji/common/index.dart';
import 'index.dart';// 1. SliverPersistentHeaderDelegate:必须实现的抽象类
class _StickyTabBarDelegate extends SliverPersistentHeaderDelegate {final Widget child;_StickyTabBarDelegate({required this.child});@overrideWidget build(BuildContext context, double shrinkOffset, bool overlapsContent) {// shrinkOffset: 滚动距离// overlapsContent: 是否与其他内容重叠return Container(color: AppTheme.pageBgColor,child: child,);}@overridedouble get maxExtent => 92.w; // 最大高度,已知tab高度72+上下padding:10@overridedouble get minExtent => 92.w; // 最小高度@overridebool shouldRebuild(covariant SliverPersistentHeaderDelegate oldDelegate) => true;
}class RankingPage extends GetView<RankingController> {const RankingPage({super.key});// 头部皇冠位置Widget _buildHeader() {return <Widget>[].toRow().card(color: AppTheme.pageBgColor).tight(width: 750.w,height: 300.w,);}// tab,可吸顶Widget _buildTab() {return <Widget>[<Widget>[TextWidget.body('日榜', size: 28.sp, weight: FontWeight.w600, color: AppTheme.textColorfff),].toRow(mainAxisAlignment: MainAxisAlignment.center).card(color: AppTheme.primaryYellow).tight(width: 336.w,height: 72.w,),<Widget>[TextWidget.body('总榜', size: 28.sp, weight: FontWeight.w600, color: AppTheme.textColor6a7),].toRow(mainAxisAlignment: MainAxisAlignment.center).card(color: AppTheme.navBarBgColor).tight(width: 336.w,height: 72.w,),].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween);}// 数据列表Widget _buildDataList() {return SliverList(delegate: SliverChildBuilderDelegate((context, index) {return <Widget>[].toRow().paddingHorizontal(30.w).card(color: AppTheme.blockBgColor).tight(width: 690.w,height: 120.w,).marginOnly(bottom: 20.w);},childCount: 20,),);}// 主视图Widget _buildView() {return SmartRefresher(controller: controller.refreshController,enablePullUp: true,onRefresh: controller.onRefresh,onLoading: controller.onLoading,footer: const SmartRefresherFooterWidget(),header: const SmartRefresherHeaderWidget(),child: CustomScrollView(slivers: [// 头部_buildHeader().sliverToBoxAdapter().sliverPaddingHorizontal(30.w),// 2. SliverPersistentHeader:实现吸顶的核心组件SliverPersistentHeader(pinned: true,  // 设置为 true 实现吸顶delegate: _StickyTabBarDelegate(child: Container(padding: EdgeInsets.symmetric(horizontal: 30.w, vertical: 10.w),child: _buildTab(),),),),// 列表内容_buildDataList().sliverPaddingHorizontal(30.w),],),);}@overrideWidget build(BuildContext context) {return GetBuilder<RankingController>(init: RankingController(),id: "ranking",builder: (_) {return Scaffold(backgroundColor: AppTheme.pageBgColor, // 自定义颜色appBar: const TDNavBar(height: 0,titleColor: AppTheme.textColorfff,titleFontWeight: FontWeight.w600,backgroundColor: AppTheme.pageBgColor,screenAdaptation: true, // 是否进行屏幕适配,默认trueuseDefaultBack: false,),body: _buildView(),);},);}
}
GetTickerProviderStateMixin 和 GetSingleTickerProviderStateMixin 的主要区别在于:
1、GetSingleTickerProviderStateMixin:
用于创建单个 AnimationController
性能更好,因为只维护一个 Ticker
适用场景:只需要一个动画控制器的情况,如单个 TabController
class MyController extends GetxController with GetSingleTickerProviderStateMixin {late TabController tabController;@overridevoid onInit() {super.onInit();tabController = TabController(length: 3, vsync: this);}@overridevoid onClose() {tabController.dispose();super.onClose();}
}2、GetTickerProviderStateMixin:
可以创建多个 AnimationController
适用场景:需要多个独立动画控制器的情况class MyController extends GetxController with GetTickerProviderStateMixin {late AnimationController controller1;late AnimationController controller2;@overridevoid onInit() {super.onInit();controller1 = AnimationController(vsync: this,duration: const Duration(milliseconds: 300),);controller2 = AnimationController(vsync: this,duration: const Duration(milliseconds: 500),);}
}什么时候需要 GetSingleTickerProviderStateMixin:
当你使用 TabController 来控制 tab 切换动画
当你需要实现滑动切换 tab 功能
当你需要更复杂的 tab 切换效果如果只需要点击切换,不需要 GetSingleTickerProviderStateMixin

相关文章:

Flutter:自定义Tab切换,订单列表页tab,tab吸顶

1、自定义tab切换 view <Widget>[// 好评<Widget>[TDImage(assetUrl: assets/img/order4.png,width: 36.w,height: 36.w,),SizedBox(width: 10.w,),TextWidget.body(好评,size: 24.sp,color: controller.tabIndex 0 ? AppTheme.colorfff : AppTheme.color999,),]…...

SAS-proc sgplot绘图

1、绘图-直条图示例&#xff1a; 1.1 数据集 1.2 代码 proc sgplot data sashelp.cars;vbar origin / response msrp /* response&#xff1a;响应变量&#xff0c;Y轴 */stat mean /* stat&#xff1a;统计量&#xff0c;结果用均值呈现 */group type /* group&#…...

怎么使用python 调用高德地图api查询位置和导航?

环境&#xff1a; python 3.10 问题描述&#xff1a; 怎么使用python 调用高德地图api查询位置和导航? 解决方案&#xff1a; 要使用Python调用高德地图API查询位置和导航&#xff0c;需要先注册高德开发者账号并获取API Key。以下是基本步骤&#xff1a; 1. 注册高德开…...

pikachu靶场-敏感信息泄露概述

敏感信息泄露概述 由于后台人员的疏忽或者不当的设计&#xff0c;导致不应该被前端用户看到的数据被轻易的访问到。 比如&#xff1a; ---通过访问url下的目录&#xff0c;可以直接列出目录下的文件列表; ---输入错误的url参数后报错信息里面包含操作系统、中间件、开发语言的版…...

使用ssh推送项目到github

文章目录 1. 确保已生成 SSH 密钥2. 在 GitHub 上创建远程仓库3. 初始化本地项目4. 将本地项目与远程仓库关联5. 添加文件并提交补充&#xff1a;拉取远程修改&#xff08;可选&#xff09;6. 推送到 GitHub7. 完成总结 出现的问题解决方法&#xff1a;方法 1&#xff1a;允许合…...

SAP MRP运行出现例外消息怎么处理?例外消息的优先级、案例分享

【SAP系统PP模块研究】 #SAP #PP #MRP #例外消息 #MRP评估 一、MRP评估中的例外消息 例外消息&#xff0c;是SAP系统在MRP运行过程中自动产生的消息。对例外消息检查其产生的原因&#xff0c;及时与销售、生产、采购等相关部门进行沟通&#xff0c;并进行相应调整&#x…...

002-SpringBoot整合AI(Alibaba)

SpringBoot整合AI 一、引入依赖二、配置application.yml三、获取 api-key四、编写 controller五、起服务调用 一、引入依赖 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><vers…...

Java中如何安全地停止线程?

大家好&#xff0c;我是锋哥。今天分享关于【Java中如何安全地停止线程?】面试题。希望对大家有帮助&#xff1b; Java中如何安全地停止线程? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在Java中&#xff0c;安全地停止线程是一项重要的任务&#xff0c;尤其…...

Apache Tomcat文件包含漏洞复现(详细教程)

1.漏洞原理 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器&#xff0c;其安装后会默认开启ajp连接器&#xff0c;方便与其他web服务器通过ajp协议进行交互。属于轻量级应用服务器&#xff0c;在中小型系统和并发访问用户不是很多的场合下被普遍使用&#xff0c;是开发…...

个人学习 - 什么是Vim?

观我往旧&#xff0c;同我仰春 - 2025.1.10 声明 仅作为个人学习使用&#xff0c;仅供参考 本文所有解释参考笔者个人理解&#xff0c;最终目的是服务于自我学习&#xff0c; 如果你需要了解官方更规范的解释&#xff0c;请自行查阅 Vim 是什么 Vim 是一个强大的 文本编辑器…...

Flink Gauss CDC:深度剖析存量与增量同步的创新设计

目录 设计思路 1.为什么不直接用FlinkCDC要重写Flink Gauss CDC 2.存量同步的逻辑是什么 2.1、单主键的切片策略是什么 2.2、​​​​​复合主键作切片,怎么保证扫描到所有的数据 3、增量同步的逻辑是什么 4、存量同步结束之后如何无缝衔接增量同步 5、下游数据如何落…...

docker 部署.netcore应用优势在什么地方?

目录 1. 环境一致性 2. 简化依赖管理 3. 快速部署与扩展 4. 资源利用率高 5. 版本控制与回滚 6. 安全性 7. 生态系统支持 8. 微服务架构支持 9. 降低成本 10. 开发体验提升 总结 使用 Docker 部署 .NET Core 应用有许多优势&#xff0c;特别是在开发、测试和生产环境…...

AIP-126 枚举

编号126原文链接AIP-126: Enumerations状态批准创建日期2019-07-24更新日期2019-07-24 一个域的值集合是一组数量有限的具体值&#xff0c;这是很常见的。此时使用枚举&#xff08;缩写为“enums”&#xff09;可有助于明确表达值集合的范围。 指南 API 可以 为不经常更改的…...

P3707 [SDOI2017] 相关分析 Solution

Description 给定序列 x ( x 1 , x 2 , ⋯ , x n ) , y ( y 1 , y 2 , ⋯ , y n ) x(x_1,x_2,\cdots,x_n),y(y_1,y_2,\cdots,y_n) x(x1​,x2​,⋯,xn​),y(y1​,y2​,⋯,yn​)&#xff0c;有 m m m 个操作&#xff0c;分三种&#xff1a; query ⁡ ( l , r ) \operatornam…...

Android AutoMotive --CarService

1、AAOS概述 Android AutoMotive OS是谷歌针对车机使用场景打造的操作系统&#xff0c;它是基于现有Android系统的基础上增加了新特性&#xff0c;最主要的就是增加了CarService&#xff08;汽车服务&#xff09;模块。我们很容易把Android AutoMotive和Android Auto搞混&…...

K8S中Service详解(三)

HeadLiness类型的Service 在某些场景中&#xff0c;开发人员可能不想使用Service提供的负载均衡功能&#xff0c;而希望自己来控制负载均衡策略&#xff0c;针对这种情况&#xff0c;kubernetes提供了HeadLiness Service&#xff0c;这类Service不会分配Cluster IP&#xff0c;…...

C++----STL(vector)

vector的介绍 vector的文档介绍&#xff1a;cplusplus.com/reference/vector/vector/ 1.基本概念 简单来说&#xff0c;vector是表示可以改变大小的数组的顺序容器。使用连续的存储位置来存储元素&#xff0c;因此可以通过常规指针的偏移量来高效访问。 2.内部机制 vector…...

Ubuntu24.04初始化MySQL报错 error while loading shared libraries libaio.so.1

Ubuntu24.04初始化MySQL报错 error while loading shared libraries: libaio.so.1 问题一&#xff1a;libaio1不存在 # 提示libaio1不存在 [rootzabbix-mysql-master.example.com x86_64-linux-gnu]#apt install numactl libaio1 Reading package lists... Done Building depe…...

初探大数据流式处理

文章目录 初探大数据流式处理批式处理系统特点流式处理系统特点大批次计算微批次计算适用场景 流式计算的应用场景流式大数据的特征流式计算的关键技术流式处理框架的特征三大流式数据处理框架 初探大数据流式处理 大数据处理系统主要分为批式处理和流式处理两类。批式处理将大…...

【Linux】Linux入门(三)权限

目录 前提权限概念whoami指令 Linux权限管理文件访问者的分类&#xff08;人&#xff09;file指令权限信息权限的表示方法 chmod指令 更改权限chown指令 修改文件&#xff0c;文件夹所属用户和用户组 权限掩码umask&#xff08;权限掩码&#xff09; 粘滞位 前提 请先看下面这…...

vscode里如何用git

打开vs终端执行如下&#xff1a; 1 初始化 Git 仓库&#xff08;如果尚未初始化&#xff09; git init 2 添加文件到 Git 仓库 git add . 3 使用 git commit 命令来提交你的更改。确保在提交时加上一个有用的消息。 git commit -m "备注信息" 4 …...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

树莓派超全系列教程文档--(62)使用rpicam-app通过网络流式传输视频

使用rpicam-app通过网络流式传输视频 使用 rpicam-app 通过网络流式传输视频UDPTCPRTSPlibavGStreamerRTPlibcamerasrc GStreamer 元素 文章来源&#xff1a; http://raspberry.dns8844.cn/documentation 原文网址 使用 rpicam-app 通过网络流式传输视频 本节介绍来自 rpica…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...