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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/893655.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(一)

Understanding Diffusion Models: A Unified Perspective&#xff08;一&#xff09; 文章概括引言&#xff1a;生成模型背景&#xff1a;ELBO、VAE 和分层 VAE证据下界&#xff08;Evidence Lower Bound&#xff09;变分自编码器 &#xff08;Variational Autoencoders&#x…

QT6 + CMAKE编译OPENCV3.9

参考文档 [1] https://blog.csdn.net/rjkf_css/article/details/135676077 前提条件 配置好相关运行环境&#xff1a;QT6、OPENCV3.9的sources文件 OPENCV下载网页&#xff1a;https://opencv.org/releases/ QT6下载教程&#xff1a;https://blog.csdn.net/caoshangpa/article…

Zemax 非序列模式下的颜色检测器和颜色混合

在 Zemax 的非序列模式中&#xff0c;探测器用于捕获系统中射线的信息。可以使用各种类型的探测器来捕获光学系统性能的不同方面&#xff0c;例如矩形探测器&#xff0c;它存储撞击它的 NSC 源射线的能量数据。 另一种经常使用的探测器类型是 Detector Color&#xff0c;它是一…

金融级分布式数据库如何优化?PawSQL发布OceanBase专项调优指南

前言 OceanBase数据库作为国产自主可控的分布式数据库&#xff0c;在金融、电商、政务等领域得到广泛应用&#xff0c;优化OceanBase数据库的查询性能变得愈发重要。PawSQL为OceanBase数据库提供了全方位的SQL性能优化支持&#xff0c;助力用户充分发挥OceanBase数据库的性能潜…

uni-app 程序打包 Android apk、安卓夜神模拟器调试运行

1、打包思路 云端打包方案&#xff08;每天免费次数限制5&#xff0c;最简单&#xff0c;可以先打包尝试一下你的程序打包后是否能用&#xff09;&#xff1a; HBuilderX 发行App-Android云打包 选择Android、使用云端证书、快速安心打包本地打包&#xff1a; HBuilderX …

小利特惠源码/生活缴费/电话费/油卡燃气/等充值业务类源码附带承兑系统

全新首发小利特惠/生活缴费/电话费/油卡燃气/等充值业务类源码附带U商承兑系统 安装教程如下 图片:

MacOS安装Docker battery-historian

文章目录 需求安装battery-historian实测配置国内源相关文章 需求 分析Android电池耗电情况、唤醒、doze状态等都要用battery-historian&#xff0c; 在 MacOS 上安装 battery-historian&#xff0c;可以使用 Docker 进行安装runcare/battery-historian:latest。装完不需要做任…

二叉树(了解)c++

二叉树是一种特殊的树型结构&#xff0c;它的特点是: 每个结点至多只有2棵子树(即二叉树中不存在度大于2的结点) 并且二叉树的子树有左右之分&#xff0c;其次序不能任意颠倒&#xff0c;因此是一颗有序树 以A结点为例&#xff0c;左边的B是它的左孩子&#xff0c;右边的C是…

亚博microros小车-原生ubuntu支持系列:7-脸部检测

背景知识 官网介绍&#xff1a; Face Mesh - mediapipe mpFaceMesh.FaceMesh() 类的参数有&#xff1a;self.staticMode, self.maxFaces, self.minDetectionCon, self.minTrackCon staticMode:是否将每帧图像作为静态图像处理。如果为 True&#xff0c;每帧都会进行人脸检测…

想品客老师的第六天:函数

函数基础的部分写在这里 函数声明 在js里万物皆对象&#xff0c;函数也可以用对象的方式定义 let func new Function("title", "console.log(title)");func(我是参数title); 也可以对函数赋值&#xff1a; let cms function (title) {console.log(tit…

leetcode刷题记录(八十一)——236. 二叉树的最近公共祖先

&#xff08;一&#xff09;问题描述 236. 二叉树的最近公共祖先 - 力扣&#xff08;LeetCode&#xff09;236. 二叉树的最近公共祖先 - 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。百度百科 [https://baike.baidu.com/item/%E6%9C%80%E8%BF%91%E5%85%AC%E5%85%B…

Spring Boot整合JavaMail实现邮件发送

一. 发送邮件原理 发件人【设置授权码】 - SMTP协议【Simple Mail TransferProtocol - 是一种提供可靠且有效的电子邮件传输的协议】 - 收件人 二. 获取授权码 开通POP3/SMTP&#xff0c;获取授权码 授权码是QQ邮箱推出的&#xff0c;用于登录第三方客户端的专用密码。适用…

AIGC数智化赋能:创新地方文旅内容生产传播模式

随着人工智能技术的迅猛发展&#xff0c;AI的应用领域日益扩大。当前&#xff0c;如何将AI这一新质生产力转化为新质传播力和影响力&#xff0c;进而为城市文化和旅游产业的内容创造、传播及消费模式带来全面革新&#xff0c;已成为数字化文旅发展的关键议题。 AI宣传——提升…

Tensor 基本操作4 理解 indexing,加减乘除和 broadcasting 运算 | PyTorch 深度学习实战

前一篇文章&#xff0c;Tensor 基本操作3 理解 shape, stride, storage, view&#xff0c;is_contiguous 和 reshape 操作 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started Tensor 基本使用 索引 indexing示例代码 加减…

STM32 硬件I2C读写

单片机学习&#xff01; 目录 前言 一、步骤 二、配置I2C外设 2.1 开启I2C外设和GPIO口时钟 2.2 GPIO口初始化为复用开漏模式 2.3 结构体配置I2C 2.4 使能I2C 2.5 配置I2C外设总代码 三、指定地址写时序 3.1 生产起始条件S 3.2 监测EV5事件 3.3 发送从机地址 3.4 …

使用 Elasticsearch 导航检索增强生成图表

作者&#xff1a;来自 Elastic Louis Jourdain 及 Ivan Monnier 了解如何使用知识图谱来增强 RAG 结果&#xff0c;同时在 Elasticsearch 中高效存储图谱。本指南探讨了根据用户查询动态生成知识子图的详细策略。 检索增强生成 (RAG) 通过将大型语言模型 (LLM) 的输出基于事实数…

【后端开发】字节跳动青训营之性能分析工具pprof

性能分析工具pprof 一、测试程序介绍二、pprof工具安装与使用2.1 pprof工具安装2.2 pprof工具使用 资料链接&#xff1a; 项目代码链接实验指南pprof使用指南 一、测试程序介绍 package mainimport ("log""net/http"_ "net/http/pprof" // 自…

【落羽的落羽 数据结构篇】算法复杂度

文章目录 一、数据结构和算法简介二、算法复杂度1. 时间复杂度2. 空间复杂度 一、数据结构和算法简介 数据结构是计算机存储、组织数据的方式&#xff0c;指相互之间存在一种或多种特定关系的数据元素的集合。没有一种单一的数据结构对所有用途都有用&#xff0c;所以我们要学…

VsCode安装文档

一、下载 进入VS Code官网&#xff1a;Visual Studio Code - Code Editing. Redefined&#xff0c;点击 DownLoad for Windows下载windows版本 当然也可以点击旁边的箭头&#xff0c;下载Windows版本 或 Mac OS 版本 备注&#xff1a; Stable&#xff1a;稳定版Insiders&#…

32、【OS】【Nuttx】OSTest分析(1):stdio测试(二)

背景 接上篇wiki 31、【OS】【Nuttx】OSTest分析&#xff08;1&#xff09;&#xff1a;stdio测试&#xff08;一&#xff09; 继续stdio测试的分析&#xff0c;上篇讲到标准IO端口初始化&#xff0c;单从测试内容来说其实很简单&#xff0c;没啥可分析的&#xff0c;但这几篇…