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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35649.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35649.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35649.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Flask 框架集成Bootstrap

前面學習了 Flask 框架的基本用法&#xff0c;以及模板引擎 Jinja2&#xff0c;按理說可以開始自己的 Web 之旅了&#xff0c;不過在啟程之前&#xff0c;還有個重要的武器需要了解一下&#xff0c;就是著名的 Bootstrap 框架和 Flask 的結合&#xff0c;這將大大提高開發 Web …

國產數據庫-內核特性-低基數全局字典

國產數據庫-內核特性-StarRocks低基數全局字典 StarRocks2.0引入了低基數全局字典&#xff0c;可以通過全局字典將字符串的相關操作轉換成整型相關操作&#xff0c;大大提升查詢性能。 1、低基數字典 對于利用整型替代字符串進行處理&#xff0c;通常使用字典編碼進行優化。Sta…

人大金倉助力某大型金融機構業務系統異地容災優化升級

日前&#xff0c;人大金倉助力某大型金融機構應收賬款融資服務平臺異地容災項目順利上線&#xff0c;保證了平臺系統運行的連續性和數據安全&#xff0c;為充分發揮平臺的融資功能&#xff0c;緩解中小微企業融資難提供了強有力的保障。 “ 緩解中小微企業融資難 某大型金融機構…

【MySQL--->數據庫操作】

文章目錄 [TOC](文章目錄) 一、操作語句1.增2.刪3.改4.查5.備份 二、字符集與校驗規則 一、操作語句 1.增 語句格式:create database [if no exists]數據庫名[create_specification [,create_specification] …]; 中括號內是可選項,if no exists是指如果數據庫不存在就創建,存…

STM32 F103C8T6學習筆記7:雙機無線串口通信

今日嘗試配通倆個C8T6單片機之間的無線串口通信&#xff0c;文章提供原理&#xff0c;源碼&#xff0c;測試效果圖&#xff0c;測試工程下載&#xff1a; 目錄 傳輸不規范問題&#xff1a; 串口通信資源&#xff1a; 單個串口資源理解&#xff1a; 單片機串口資源&#xf…

Redis的單線程與多線程

Redis的核心處理邏輯一直都是單線程 有一些分支模塊是多線程(某些異步流程從4.0開始用的多線程&#xff0c;例如UNLINK、FLUSHALL ASYNC、FLUSHDB ASYNC等非阻塞的刪除操作。網絡I/O解包從6.0開始用的是多線程;) 為什么是單線程 多線程多好啊可以利用多核優勢 官方給的解釋 …

UI自動化環境的搭建(python+pycharm+selenium+chrome)

最近在做一些UI自動化的項目&#xff0c;為此從環境搭建來從0到1&#xff0c;希望能夠幫助到你&#xff0c;同時也是自我的梳理。將按照如下進行開展&#xff1a; 1、python的下載、安裝&#xff0c;python環境變量的配置。 2、pycharm開發工具的下載安裝。 3、selenium的安裝。…

Leetcode34 在排序數組中查找元素的第一個和最后一個位置

給你一個按照非遞減順序排列的整數數組 nums&#xff0c;和一個目標值 target。請你找出給定目標值在數組中的開始位置和結束位置。 如果數組中不存在目標值 target&#xff0c;返回 [-1, -1]。 你必須設計并實現時間復雜度為 O(log n) 的算法解決此問題。 代碼&#xff1a; c…

如何使用 Go 獲取 URL 的參數,以及使用時的問題

Go 獲取 URL 參數也很容易&#xff0c;但是由于 Go 有嚴格的數據類型和錯誤管理&#xff0c;所以在使用時會些微有些復雜。所以本文不僅會講如何獲取 URL 的參數&#xff0c;也會講在使用時的一些問題。 首先假設 URL 是https://www.example.com/?keywordabc&id12。 其他…

java中函數式接口、Stream流、方法引用、junit單元測試、反射、注解

函數式接口&#xff1a; 在java中有且僅有一個抽象方法的接口稱為函數式接口&#xff0c;但是可以包含其它的默認的或靜態的方法。 格式&#xff1a; 修飾符 interface 接口名稱 {public abstract 返回值類型 方法名稱(可選參數);// 其他非抽象方法 }函數式接口&#xff1a;…

服務器安全維護注意事項有哪些?

服務器的安全關系著公司整個網絡以及所有數據的安全&#xff0c;我們該如何做好服務器后續的安全維護呢?河南億恩科技股份有限公司&#xff0c;專注服務器托管23年&#xff0c;不僅是國內專業的互聯網基礎應用服務提供商之一&#xff0c;還是國家工信部認定的綜合電信服務運營…

OpenJDK Maven 編譯出錯: package jdk.nashorn.internal.runtime.logging does not exist

前言 OpenJDK 1.8.0Maven 3.8.5TencentOS Server 3.1 錯誤信息 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.1:compile (default-compile) on project tour-common: Compilation failure: Compilation failure: [ERROR] /opt/tour-c…

JavaScript預編譯機制

變量預編譯 任何變量&#xff0c;如果未經聲明就賦值&#xff0c;此變量是屬于 window 的屬性&#xff0c;而且不會做變量提升&#xff0c;無論在哪個作用域內賦值。比如說直接寫 console.log(a)肯定會報錯&#xff0c;提示找不到 a。但如果直接寫 a 100就不會報錯&#xff0…

【Linux命令行與Shell腳本編程】第十九章 正則表達式

Linux命令行與Shell腳本編程 第十九章 正則表達式 文章目錄 Linux命令行與Shell腳本編程 第十九章 正則表達式九.正則表達式9.1.正則表達式基礎9.1.1.正則表達式的類型9.2.定義BRE模式9.2.1.普通文本9.2.2.特殊字符 9.2.3.錨點字符錨定行首^錨定行尾$組合錨點 9.2.4.點號字符\.…

funbox3靶場滲透筆記

funbox3靶場滲透筆記 靶機地址 https://download.vulnhub.com/funbox/Funbox3.ova 信息收集 fscan找主機ip192.168.177.199 .\fscan64.exe -h 192.168.177.0/24___ _/ _ \ ___ ___ _ __ __ _ ___| | __/ /_\/____/ __|/ __| __/ _ |/ …

SpringBoot復習(39)Servlet容器的自動配置原理

Servlet容器自動配置類為ServletWebServerFactoryAutoConfiguration 可以看到通過Import注解導入了三個配置類&#xff1a; 通過這個這三個配置類可以看出&#xff0c;它們都使用了ConditionalOnClass注解&#xff0c;當類路徑存在tomcat相關的類時&#xff0c;會配置一個T…

【數據結構?堆】序列和的前n小元素

題目描述 問題&#xff1a;序列和的前n小元素   給出兩個長度為n的有序表A和B, 在A和B中各任取一個, 可以得到 n^2 個和. 求這些和最小的n個。 輸入輸出格式 輸入格式&#xff1a; 輸入數據共三行。   第一行&#xff0c;一個整數值n &#xff08; n < 10^4 &#xff…

Linux系列:從0到1用Docker部署springboot項目

目錄 1.前提條件 2.編寫DockerFile鏡像文件 3.打包SpringBoot項目 4.通過軟件Xftp進行傳輸&#xff08;*&#xff09; 1.點擊“文件-新建”?編輯 5.操作遠程主機 1.docker構建 2.容器運行 6.容器的關閉和刪除 1.前提條件 Linux、docker、xftp的安裝、一臺可以訪問的遠…

教雅川學纏論07-中樞實戰眾泰汽車000980

本文實戰眾泰汽車 下面是2023年11月14-2023年8月8眾泰汽車日K圖 先畫日K 接下來處理包含&#xff0c;就變成下面這個樣子 下面在套上纏論的理論&#xff0c;未來股價的走勢應該是紅色橢圓形虛線里面的樣子 好了&#xff0c;文章就到這里&#xff0c;如果眾泰最終不是這個走勢…

linux 目錄操作命令

目錄操作命令 文件列表 ls命令文件列表 ls [選項] [參數]-------------------------------l 詳細信息-L 緊接著符號性連接&#xff0c;列出它們指向的文件-a 所有文件&#xff0c;包含隱藏文件(以點號起始的文件)-A 與-a相同&#xff0c;但是不會列出來. 和 ..-c 根據創建時間排…