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

Flutter BottomSheet 三段式拖拽

BottomSheetBehavior

  • 追踪 BottomSheet
  • 系统默认实现效果
  • 准备要实现的功能点:
    • 定义三段式状态:BottomSheetBehavoir
    • 阀值定义
      • 1. 未达到滚动阀值,恢复状态
      • 2. 达到滚动阀值,更新状态

前面倒是有讲过Android原生的BottomSheetBehavior,使用场景还是蛮多的,最近在用Flutter做一款地图App,有用到BottomSheet的功能,但是 Flutter 自带的BottomSheet有点拉,只能显示和隐藏销毁,不支持折叠为最小高度状态也不支持三段式拖动,那就自己撸一个吧:

追踪 BottomSheet

既然是基于系统的BottomSheet ,不妨来看看sdk的实现方式,正常来讲,显示一个BottomSheet,可以通过showBottomSheet 来触发,或者给Scaffold配置bottomSheet属性,查看源码可以看到Scaffold.of(context).showBottomSheet,内部是创建了一个_StandardBottomSheet,继续追踪发现Widget其实是通过AnimatedBuilder来实现内容高度的扩展,其内部维护了一个BottomSheet。

简单阅读下BottomSheet源码,重点就在于 GestureDetector 的垂直方向上的手势回调 onVerticalDragUpdate 、以及onVerticalDragEnd,拖动位置更新、惯性滑动以及销毁,核心都在这了。
在这里插入图片描述

系统默认实现效果

  • 拖拽速度大于某一个像素阀值时,销毁。
  • 拖拽位置小于总高度的一半时,销毁。

保留这一份默认效果,对于想使用默认效果的同学,不做任何额外配置即可。

在这里插入图片描述

准备要实现的功能点:

  1. 三段式: 基于SDK的BottomSheet ,可扩展为完全展开、中间状态、折叠状态;
  2. 阻尼、惯性滑动: 支持配置最小滑动偏移量;
  3. 保持状态,支持Peek状态: 以最小高度显示BottomSheet;
  4. 打破 showBottomSheet 限制: 兼容系统默认的弹出方式,亦可当作正常的Widget使用,脱离showBottomSheet。

定义三段式状态:BottomSheetBehavoir

  • EXPANDED 完全展开
  • HALF 中间状态,介于EXPANDED与PEEK之间
  • PEEK 以一个最小高度展示
  • HIDDEN 完全隐藏,即销毁,系统默认效果

开启三段式,我们还需要配置一个约束条件,即BottomSheet的最大高度和最小高度 BoxConstraints:

  • 最小高度
    HALF模式下
    如果提供的 Constraints minHeight 小于最大高度的一半,则取后者,防止位置错乱!
var peekThreshold = enableHalf? min(_childHeight / 2, constraints.minHeight) / _childHeight: constraints.minHeight / _childHeight;

阀值定义

  • 拖拽滚动阀值,大于此值,才允许滑动
    const double _offsetThreshold = 32.0;
  • 展开时最大高度 阀值
    const double _maxThreshold = 1.0;
  • 中间状态阀值
    const double _halfThreshold = 0.5;

当拖拽结束时,如果拖拽偏移量小于此阀值,则恢复状态,这里有个麻烦的点是需要根据用户拖拽方向来判断,是向上还是向下拖动。
方向判断可以在 _handleDragStart 回调时记录初始偏移量startY,_handleDragEnd 时计算开始和结束的差值

/// 偏移量
var offset = updateY-startY ;
/// 当前动画值var value = widget.animationController!.value;late double toValue;late BottomSheetBehavior mode;

offset<0 为向上滑动,反之 向下滑动。接下来需要根据滚动阀值来更新BottomSheet状态。

1. 未达到滚动阀值,恢复状态

  • 向上滑动,恢复BottomSheet状态: Expanded / Half / Peek
if (value >= _maxThreshold) {// 处于Expand状态,恢复toValue = _maxThreshold;mode = BottomSheetBehavior.EXPANDED;} else if (value > _halfThreshold && enableHalf) {// 处于Half,恢复toValue = _halfThreshold;mode = BottomSheetBehavior.HALF;} else {toValue = peekThreshold;mode = BottomSheetBehavior.PEEK;}
  • 向下滑动,恢复BottomSheet状态: Expanded / Half / Peek
if (value > _halfThreshold) {// 处于Expand状态,恢复toValue = _maxThreshold;mode = BottomSheetBehavior.EXPANDED;} else if (value > peekThreshold && enableHalf) {// 处于Half,恢复toValue = _halfThreshold;mode = BottomSheetBehavior.HALF;} else {toValue = peekThreshold;mode = BottomSheetBehavior.PEEK;}

2. 达到滚动阀值,更新状态

  • 向上滑动,更新BottomSheet状态: Expanded / Half / Peek
if (value > _halfThreshold) {toValue = _maxThreshold;mode = BottomSheetBehavior.EXPANDED;} else if (value > peekThreshold) {toValue = enableHalf ? _halfThreshold : _maxThreshold;mode = enableHalf ? BottomSheetBehavior.HALF : BottomSheetBehavior.EXPANDED;} else {toValue = peekThreshold;mode = BottomSheetBehavior.PEEK;}
  • 向下滑动,更新BottomSheet状态: Half / Peek
if (value > _halfThreshold) {toValue = enableHalf ? _halfThreshold : peekThreshold;mode = enableHalf ? BottomSheetBehavior.HALF : BottomSheetBehavior.PEEK;
} else {toValue = peekThreshold;mode = BottomSheetBehavior.PEEK;
}

以上,我们获取到了开始讲到的AnimatedBuilder的 动画值以及变化量,在**_handleDragEnd**中可以通过animateTo平滑的过渡BottomSheet状态

/// 以动画的形式fly
void animateTo(double to) {widget.animationController!.animateTo(to,curve: Curves.linearToEaseOut,duration: animateDuration,);
}
  • 另外,至于BottomSheet的最新的状态回调,最好是在动画结束后再通知给调用者,以免更新状态期间build重绘!
Future.delayed(animateDuration, () => widget.onBehaviorChanged?.call(mode));

至此,既保留了flutter默认的BottomSheet效果,又扩展了三段式,当然,调用方式和系统BottomSheet一模一样,另外还可以像普通Widget一样来使用哦,来看看最终的效果吧

项目效果

Demo

相关文章:

Flutter BottomSheet 三段式拖拽

BottomSheetBehavior 追踪 BottomSheet系统默认实现效果准备要实现的功能点&#xff1a;定义三段式状态&#xff1a;BottomSheetBehavoir阀值定义1. 未达到滚动阀值&#xff0c;恢复状态2. 达到滚动阀值&#xff0c;更新状态 前面倒是有讲过Android原生的BottomSheetBehavior&a…...

php后端实现调用高德地图进行POI搜索

对于当前位置或者选定省市位置进行查询 接口实现 /*** 查询地址* ApiTitle (查询地址)* ApiSummary (查询地址)* ApiMethod (POST)* ApiRoute (/api/demo/address)* ApiParams (name"dart", type"integer", requiredtrue, description"省…...

uniapp 实现滑动视图切换 顶部滚动导航栏

无论小程序的时候一般有这个功能,在页面处于首页时候,滑动视图,切换视图顶部滚动导航也跟着切换 1.想要实现这个功能就需要实现顶部导航栏,首先实现顶部滚导航栏 点击高亮颜色显示 模板代码 <scroll-view scroll-x"true" class"scroll-content" > …...

ArcGIS API for JavaScript 调用自定义地图模板总结

ArcGIS API for JavaScript 调用自定义地图模板总结 3.9版本4.24版本 3.9版本 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><title>Hello World</title><link rel"stylesheet" href&qu…...

QGraphicsView实现简易地图5『经纬网格』

前文链接&#xff1a;QGraphicsView实现简易地图4『局部加载-地图漫游』 由于GCJ02 Web 墨卡托投影 纬度并不随像素等分&#xff0c;且两极跨度较大&#xff0c;因此本次演示采用的经纬网等分逻辑为等分像素。同等像素跨度之间&#xff0c;两级纬度变化较小&#xff0c;越靠近赤…...

RestTemplate 请求转发异常 ERR_CONTENT_DECODING_FAILED 200 (OK)

#1 问题描述 在基于Spring Boot的项目中实现了请求转发&#xff08;使用 RestTemplate 的 exchange 方法&#xff09;的功能&#xff0c;忽然在前端报net::ERR_CONTENT_DECODING_FAILED 200 (OK)的错误&#xff0c;后端及上游系统日志均显示请求已完成。 #2 原因探寻 上述错…...

用python实现一个异或计算器

有这样一条需求&#xff1a;计算某个文件中的数组每一行元素的最后一个参数&#xff0c;异或输出。 因为元素比较多&#xff0c;十几行&#xff0c;通过人工去计算异或值非常困难。 而在线异或的计算器&#xff0c;也需要人为输入这些数值&#xff0c;每次计算一个最终结果需…...

Sketch打不开AI文件?转换方法在这里

1、对比设计软件 Sketch 与 AI 软件功能 Sketch 与 Illustrator 都是行业内优秀的矢量图形设计软件&#xff0c;各有千秋。Sketch 从 2010 年面世&#xff0c;专注 APP 界面设计&#xff0c;深受初学者与专业人士喜爱。Illustrator 拥有更悠久的历史&#xff0c;是处理复杂图标…...

小游戏扫雷实现教学(详解)

目录 【前言】 一、模块化程序设计&#xff08;多文件编程&#xff09;介绍 1.概述 2.传统编程的方式 3.模块化程序设计的方法 二、扫雷代码设计思路 三、扫雷代码设计 1.创建菜单函数 2.实现9x9扫雷 3.初始化棋盘 4.打印棋盘 5.随机布置雷的位置 6.排查雷的信息 7.回…...

04 mysql innodb record

前言 最近看到了 何登成 大佬的 "深入MySQL源码 -- Step By Step" 的 pdf 呵呵 似乎是找到了一些 方向 之前对于 mysql 方面的东西, 更多的仅仅是简单的使用[业务中的各种增删改查], 以及一些面试题的背诵 这里会参照 MySQL Internals Manual 来大致的看一下 i…...

Centos7安装Docker

0.安装Docker Docker 分为 CE 和 EE 两大版本。CE 即社区版&#xff08;免费&#xff0c;支持周期 7 个月&#xff09;&#xff0c;EE 即企业版&#xff0c;强调安全&#xff0c;付费使用&#xff0c;支持周期 24 个月。 Docker CE 分为 stable test 和 nightly 三个更新频道…...

Vue中如何更好地封装组件?

子组件接受父组件传递的事件 1.子组件使用事件名"$emit(父组件中传递的事件名,想给父组件传递的参数(可选))" click"$emit(click)" 2.子组件使用 v-on"$listeners" 父组件&#xff1a; <template><div id"app"><myCo…...

C语言的链表的相关操作

本变博客源于自己想复习一下C语言&#xff0c;所以便自己动手复习了一下链表的相关操作。做个人记录使用。 main.c #include <stdio.h> #include "list.h"int main() {student *a;printf("hello world\n") ;printf("----初始化列表----------\…...

Python3中typing模块

Python类型注解是Python 3.5版本之后引入的新特性&#xff0c;它可以让开发者在函数、变量等声明时为其指定类型。typing模型能够声明类型&#xff0c;防止运行时出现参数和返回值类型不符合的问题。 ### 1. 基本类型注解 def hello(name: str) -> str:return (Hello, na…...

C语言自动抓取淘宝商品详情网页数据,实现轻松高效爬虫

你是否曾经遇到过需要大量获取网页上的数据&#xff0c;但手动复制粘贴又太过费时费力&#xff1f;那么这篇文章就是为你而写。今天我们将会详细讨论如何使用C语言实现自动抓取网页上的数据。本文将会从以下8个方面进行逐步分析讨论。 1. HTTP协议的基本原理 在开始之前&…...

数据结构---跳表

目录标题 为什么会有跳表跳表的原理跳表的模拟实现准备工作find函数insert函数erase函数 测试效率比较 为什么会有跳表 在前面的学习过程中我们学习过链表这个容器&#xff0c;这个容器在头部和尾部插入数据的时间复杂度为O(1)&#xff0c;但是该容器存在一个缺陷就是不管数据…...

为什么Tomcat的NIO在读取body时要模拟阻塞?

文章首发地址 Tomcat的NIO完全可以以非阻塞方式处理IO&#xff0c;为什么在读取body部分时要模拟阻塞呢&#xff1f;在Tomcat的NIO读取HTTP请求时&#xff0c;为了保证请求的正确性和可靠性&#xff0c;需要模拟阻塞模式&#xff0c;这是因为servlet规范里定义了ServletInputSt…...

26 | 谷歌应用APP数据分析

基于kaggle公开数据集,对谷歌应用市场的APP情况进行数据探索和分析。 from kaggle: https://www.kaggle.com/lava18/google-play-store-apps 分析思路: 0、数据准备 1、数据概览 2、种类对Rating的影响 3、定价策略 4、因素相关性分析 5、用户评价 6、总结 0、数据准备 (…...

BFS 五香豆腐

题目描述 经过谢老师n次的教导&#xff0c;dfc终于觉悟了——过于腐败是不对的。但是dfc自身却无法改变自己&#xff0c;于是他找到了你&#xff0c;请求你的帮助。 dfc的内心可以看成是5*5个分区组成&#xff0c;每个分区都可以决定的的去向&#xff0c;0表示继续爱好腐败&…...

opencv实战项目 手势识别-手势控制键盘

手势识别是一种人机交互技术&#xff0c;通过识别人的手势动作&#xff0c;从而实现对计算机、智能手机、智能电视等设备的操作和控制。 1. opencv实现手部追踪&#xff08;定位手部关键点&#xff09; 2.opencv实战项目 实现手势跟踪并返回位置信息&#xff08;封装调用&am…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

select、poll、epoll 与 Reactor 模式

在高并发网络编程领域&#xff0c;高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表&#xff0c;以及基于它们实现的 Reactor 模式&#xff0c;为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。​ 一、I…...

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...

基于IDIG-GAN的小样本电机轴承故障诊断

目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) ​梯度归一化(Gradient Normalization)​​ (2) ​判别器梯度间隙正则化(Discriminator Gradient Gap Regularization)​​ (3) ​自注意力机制(Self-Attention)​​ 3. 完整损失函数 二…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

深入解析光敏传感技术:嵌入式仿真平台如何重塑电子工程教学

一、光敏传感技术的物理本质与系统级实现挑战 光敏电阻作为经典的光电传感器件&#xff0c;其工作原理根植于半导体材料的光电导效应。当入射光子能量超过材料带隙宽度时&#xff0c;价带电子受激发跃迁至导带&#xff0c;形成电子-空穴对&#xff0c;导致材料电导率显著提升。…...

华为OD机考- 简单的自动曝光/平均像素

import java.util.Arrays; import java.util.Scanner;public class DemoTest4 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint[] arr Array…...

年度峰会上,抖音依靠人工智能和搜索功能吸引广告主

上周早些时候举行的第五届年度TikTok World产品峰会上&#xff0c;TikTok推出了一系列旨在增强该应用对广告主吸引力的功能。 新产品列表的首位是TikTok Market Scope&#xff0c;这是一个全新的分析平台&#xff0c;为广告主提供整个考虑漏斗的全面视图&#xff0c;使他们能够…...