Flutter:吸顶效果

news2025/1/9 13:28:22

在分页中,实现tab吸顶。
TDNavBar的screenAdaptation: true, 开启屏幕适配。
该属性已自动对不同手机状态栏高度进行适配。我们只需关注如何实现吸顶。
在这里插入图片描述
在这里插入图片描述

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

  @override
  Widget build(BuildContext context, double shrinkOffset, bool overlapsContent) {
    // shrinkOffset: 滚动距离
    // overlapsContent: 是否与其他内容重叠
    return Container(
      color: AppTheme.pageBgColor,
      child: child,
    );
  }

  @override
  double get maxExtent => 92.w; // 最大高度,已知tab高度72+上下padding:10

  @override
  double get minExtent => 92.w; // 最小高度

  @override
  bool 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),
        ],
      ),
    );
  }


  @override
  Widget 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, // 是否进行屏幕适配,默认true
            useDefaultBack: false,
          ),
          body: _buildView(),
        );
      },
    );
  }
}

controller

import 'package:get/get.dart';
import 'package:pull_to_refresh_flutter3/pull_to_refresh_flutter3.dart';

class RankingController extends GetxController {
  RankingController();
  List items = [];

  /*
  * 分页
  * 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(["ranking"]);
  }

  // 下拉刷新
  void onRefresh() async {
    try {
      await _loadNewsSell(true);
      refreshController.refreshCompleted();
    } catch (e) {
      refreshController.refreshFailed();
    }
    update(["ranking"]);
  }
  _initData() {
    update(["ranking"]);
  }

  void onTap() {}

  // @override
  // void onInit() {
  //   super.onInit();
  // }

  @override
  void onReady() {
    super.onReady();
    _initData();
  }

  // @override
  // void onClose() {
  //   super.onClose();
  // }
}

记录tab切换

int currentTab = 0; // 当前选中的tab索引
// tab切换方法
void switchTab(int index) {
  if (currentTab == index) return;
  currentTab = index;
  items.clear();// 切换tab时重置列表数据
  refreshController.requestRefresh();
  update(["ranking"]);
}

  // tab切换
Widget _buildTab() {
  return <Widget>[
    <Widget>[
      TextWidget.body('日榜', size: 28.sp, weight: FontWeight.w600, color: controller.currentTab == 0 ?  AppTheme.textColorfff : AppTheme.textColor646),
    ].toRow(mainAxisAlignment: MainAxisAlignment.center)
    .card(color: controller.currentTab == 0 ? AppTheme.primaryYellow : AppTheme.navBarBgColor)
    .tight(width: 336.w,height: 72.w,).onTap(() {
      controller.switchTab(0);
    }),

    <Widget>[
      TextWidget.body('总榜', size: 28.sp, weight: FontWeight.w600, color: controller.currentTab == 1 ?  AppTheme.textColorfff : AppTheme.textColor646),
    ].toRow(mainAxisAlignment: MainAxisAlignment.center)
    .card(color: controller.currentTab == 1 ? AppTheme.primaryYellow : AppTheme.navBarBgColor )
    .tight(width: 336.w,height: 72.w,).onTap(() {
      controller.switchTab(1);
    }),

  ].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween);
}

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

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

相关文章

数据结构基础之《(13)—前缀树》

一、前缀树 1、前缀树&#xff08;Trie&#xff09;&#xff0c;又称字典树或单词查找树&#xff0c;是一种用于存储字符串集合的数据结构。 2、前缀树的操作 &#xff08;1&#xff09;单个字符串中&#xff0c;字符从前到后的加到一棵多叉树上 &#xff08;2&#xff09;字…

道品科技智慧农业与云平台:未来农业的变革之路

随着全球人口的不断增长&#xff0c;农业面临着前所未有的挑战。如何在有限的土地和资源上提高农业生产效率&#xff0c;成为了各国政府和农业从业者亟待解决的问题。智慧农业的兴起&#xff0c;结合云平台的应用&#xff0c;为农业的可持续发展提供了新的解决方案。 ## 一、智…

【Linux基础指令】第一期

一、Linux的介绍 Linux是一个开源的操作系统&#xff0c;性能、稳定性、安全性方面上都是很优秀的&#xff0c;所以它一直是企业后端系统的首选。所以其图形化界面并不是Linux的必需品&#xff0c;所以我们避免不了要使用命令行的形式来使用Linux&#xff0c;也就离不开…

​​​​​​芯盾时代以数据为核心的车联网业务安全解决方案

芯盾时代车联网业务安全建设聚焦智能网联业务运行过程产生的多维度、多模态、多视角数据以及因业务需求产生的过程数据和业务衍生数据&#xff0c;以网络安全等级保护为基础&#xff0c;坚持网络安全管理体系和技术体系并重的原则&#xff0c;加强网络安全体系化、实战化、常态…

剖析 Claim-Check 模式:以小传大,赋能分布式系统与微服务

1. 前言 1.1 写作背景与目的 在当今分布式系统与微服务架构盛行的时代&#xff0c;服务间的消息传递与数据交换越来越频繁。传统的消息传输在面对海量数据时&#xff0c;往往会遇到以下痛点&#xff1a; 消息体过大&#xff1a;直接通过消息队列或服务间接口发送大体量数据&…

VS2022引入sqlite数据库交互

法一&#xff1a;用官网编译好的动态库(推荐) 下载所需文件 sqlite官网地址&#xff1a;https://www.sqlite.org/howtocompile.html 下载以下的2个压缩包 第一个压缩包 sqlite-amalgamation-xxxx.zip&#xff0c;xxxx是版本号,保持一致即可&#xff0c;这里面有sqite3.h 第…

计算机的错误计算(二百零五)

摘要 基于一位读者的问题&#xff0c;提出题目&#xff1a;能用数值计算证明 吗&#xff1f;请选用不同的点&#xff08;即差别大的数&#xff09;与不同的精度。实验表明&#xff0c;大模型理解了题意。但是&#xff0c;其推理能力值得商榷。 例1. 就摘要中问题&#xff0…

设计形成从业务特点到设计模式的关联

规范和指引在应用架构、数据架构等各架构方向上形成规范性约束指导。同一个决策要点、架构单元在统一的架构原则指导下&#xff0c;会因业务特点差异有不同的实现&#xff0c;经过总结形成了最佳实践。在开展新应用的设计时&#xff0c;根据决策要点以及相关的业务特点&#xf…

深度学习blog-深刻理解线性变换和矩阵

深度学习中避免不了矩阵运算&#xff0c;或者张量&#xff08;其实是矩阵数组&#xff09;运算。卷积是矩阵加、乘法&#xff0c;注意力也是一样。本质都一样&#xff0c;所谓注意力&#xff0c;卷积、滤波&#xff0c;是对不必了解数学的人说的&#xff0c;底层都是矩阵运算&a…

C/C++程序性能测试方法综述

摘要 性能测试是软件开发中不可或缺的一部分&#xff0c;特别是在对性能要求较高的C/C程序中。本文将详细介绍多种C/C程序性能测试方法&#xff0c;包括时间复杂度分析、事后统计方法、事前分析估算方法、使用性能测试工具&#xff08;如Google Benchmark、gprof、Valgrind等&…

jmeter 中 BeanShell 预处理程序、JSR223后置处理程序使用示例

1. 各个组件如何新建的&#xff1f; 2. "http请求" 组件内容样例&#xff1a; "消息体数据" 源码&#xff1a; {"task_tag": "face_detect","image_type": "base64","extra_args": [{"model"…

电脑32位和64位之区别(Difference between 32-Bit and 64 Bit Computers)

电脑32位和64位之区别 很多小伙伴还不知道电脑32位和64位是什么意思&#xff0c;今天小编就来普及一下。 32位和64位是指电脑处理器&#xff08;CPU&#xff09;和操作系统的架构&#xff0c;决定了电脑如何处理数据、存储信息、运行程序等。 32位和64位是指电脑系统中每个处…

vue -关于浏览器localstorge数据定期清除的实现

1.实现背景 用户登录时的信息存在了localstorge中&#xff0c;但它会一直存在。一般来说&#xff0c;我们希望这个数据能够定期被清除掉&#xff0c;以下一个定时清除的实现。 2.实现原理 在用户登录时&#xff0c;将用户信息存入localstorge的同时&#xff0c;将当前时间作…

【JavaEE进阶】获取Cookie/Session

&#x1f340;Cookie简介 HTTP协议自身是属于 "⽆状态"协议. "⽆状态"的含义指的是: 默认情况下 HTTP 协议的客⼾端和服务器之间的这次通信,和下次通信之间没有直接的联系.但是实际开发中,我们很多时候是需要知道请求之间的关联关系的. 例如登陆⽹站成…

【工具变量】统计行业锦标赛激励数据集(2008-2023年)

一、数据简介 坚持创新驱动发展&#xff0c;要强化企业创新主体地位&#xff0c;发挥企业家在技术创新中的重要作用。作为企业组织内部最具有影响力的角色&#xff0c;高级管理人员拥有企业经营管理的自由裁量权&#xff0c;对企业战略决策及由此产生的经营绩效具有举足轻重的…

UVM: TLM机制

topic overview 不建议的方法&#xff1a;假如没有TLM TLM TLM 1.0 整个TLM机制下&#xff0c;底层逻辑离不开动作发起者和被动接受者这个底层的模型基础&#xff0c;但实际上&#xff0c;在验证环境中&#xff0c;任何一个组件&#xff0c;都有可能成为动作的发起者&#xff0…

Scratch023-(沙漠变绿洲)

提示&#xff1a; 知识回顾&#xff1a; 1、画笔的各个属性 2、“将笔的颜色设为”积木 3、“将笔的颜色增加”积木 文章目录 前言一、案例展示二、功能分析三、步骤拆解1.背景角色和画笔的初始化&#xff08;1&#xff09;初始化画笔2、一起绘制一个小雨滴3、绘制多个随机的小…

游戏语音趋势解析,社交互动有助于营造沉浸式体验

语音交互的新架构出现 2024 年标志着对话语音 AI 取得了突破&#xff0c;出现了结合 STT → LLM → TTS 模型来聆听、推理和回应对话的协同语音系统。 OpenAI 的 ChatGPT 语音模式将语音转语音技术变成了现实&#xff0c;引入了基于音频和文本信息进行端到端预训练的模型&…

详细全面讲解C++中重载、隐藏、覆盖的区别

文章目录 总结1、重载示例代码特点1. 模板函数和非模板函数重载2. 重载示例与调用规则示例代码调用规则解释3. 特殊情况与注意事项二义性问题 函数特化与重载的交互 2. 函数隐藏&#xff08;Function Hiding&#xff09;概念示例代码特点 3. 函数覆盖&#xff08;重写&#xff…

计算机网络之---物理层设备

什么是物理层设备 物理层设备是指负责数据在物理媒介上传输的硬件设备&#xff0c;它们主要处理数据的转换、信号的传输与接收&#xff0c;而不涉及数据的内容或意义。常见的物理层设备包括网卡、集线器、光纤收发器、调制解调器等。 物理层设备有哪些 1、网卡&#xff08;N…