網站首頁 編程語言 正文
某些頁面比如我們在選擇聯系人或者某個城市的時候需要快速定位到我們需要的選項,一般都會需要像微信通訊錄右邊有一個導航條一樣的功能,由A到Z進行快速定位,本篇文章我們將自己來實現一個跟微信通訊錄同樣的功能。
關鍵點:手勢定位滑動、列表定位、手勢、列表聯動。
準備數據,首先我們需要準備導航目錄數據,
List<String> _az = [ "☆", "A", "B", "C", "D", "E", "F", "G", "H", "I", "G", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", ];
然后列表數據,列表數據可以讓后臺給我們排序好,如果后臺不給我們排序,我們也可以自己排序進行組裝, 這里用到一個插件,根據漢字獲取拼音首字母,我們自己就可以對這些數據進行整理排序。
lpinyin: ^2.0.3
數據格式:一個NameBean對應一個字母所在的列表,這是我們存儲正式列表數據的格式。
class NameBean { String? initial;// 字母導航 List<Name>? nameList;// 內容列表 NameBean({ this.initial, this.nameList, }); } /// name : "老李" /// 這里我只放了一個name字段,以后擴展內容只需在這里新增字段就好了 class Name { String? name; Name({ this.name, }); }
接下來我們先做導航條:
實現思路: 導航條就是一個List列表,點擊、滑動、松開會有不同的交互,我們根據不同的交互來進行實現,這里我們會用到官方的GestureDetector組件,專門用來進行手勢識別。將每一個item的高度固定,通過手勢交互返回的位置數據來進行返回我們想要的目錄。比如我的每一個目錄高度設置為20.
導航條代碼:點擊或者移動的時候選中的目錄會有一個按壓效果,
GestureDetector( child: Container( margin: EdgeInsetsDirectional.only(top: 40), width: 40, // 導航條 child: ListView.builder( physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, index) { return SizedBox( height: 20, // 這里做了一個按壓或移動滑動的觸發效果 child: Container( alignment: Alignment.center, decoration: BoxDecoration( color: currentIndex == index ? Colors.redAccent : null, shape: BoxShape.circle), child: Text( _az[index], style: TextStyle( color: currentIndex == index ? Colors.white : Colors.black87, ), ), ), ); }, itemCount: _az.length, )), //手指按下觸發 豎著劃就用onVertica XXX回調 onVerticalDragDown: (DragDownDetails e) { //打印手指按下的位置(相對于屏幕) int i = e.localPosition.dy ~/ 20; // _scrollController.jumpTo(index: i); setState(() { currentIndex = i; }); }, //手指滑動時會觸發此回調 onVerticalDragUpdate: (DragUpdateDetails e) { //用戶手指滑動時,更新偏移 int i = e.localPosition.dy ~/ 20; _az.length; if (i >= 0 && i <= _az.length - 1) { if (i != currentIndex) { setState(() { // 當前選中的index 默認-1 currentIndex = i; }); print("滑動 ${_az[i]}"); } } }, // 手指抬起 onTapUp: (e) { // 手指抬起 setState(() { currentIndex = -1; }); }, // 移動取消 onVerticalDragEnd: (e) { // 移動取消 setState(() { currentIndex = -1; }); }, )
然后我們可以看到微信在滑動的時候有個字母放大氣泡會跟隨著手勢移動。
實現思路:
氣泡和導航條并列,并根據手勢位置更新上邊距即可,因為我們的導航條的每一個item的高度是固定的,所以我們就可以根據滑動的位置計算出滑動距離頂部的高度,這里氣泡可以讓UI切個背景圖,也可以自己用canvas畫一個。
氣泡繪制源碼:目前我在學習繪制組件,順便畫了一個,可能不是最佳的,但這不重要~今天的重點不是它~~~
@override void paint(Canvas canvas, Size size) { // 原點移到左下角 canvas.translate(size.width / 2, size.height / 2); Paint paint = Paint() ..color = Colors.redAccent ..strokeWidth = 2 ..style = PaintingStyle.fill; Path path = Path(); // 繪制文字 path.lineTo(0, -size.width / 2); // path.conicTo(33, -28, 20, 0, 1); path.arcToPoint(Offset(size.width / 2, 0), radius: Radius.circular(size.width / 2), largeArc: true, clockwise: true); path.close(); var bounds = path.getBounds(); canvas.save(); canvas.translate(-bounds.width / 2, bounds.height / 2); canvas.rotate(pi * 1.2); canvas.drawPath(path, paint); canvas.restore(); // 繪制文字 var textPainter = TextPainter( text: TextSpan( text: text, style: TextStyle( fontSize: 24, foreground: Paint() ..style = PaintingStyle.fill ..color = Colors.white, )), textAlign: TextAlign.center, textDirection: TextDirection.ltr); textPainter.layout(); canvas.translate(-size.width, -size.height / 2); textPainter.paint(canvas, Offset(-size.width / 2.4, size.height / 1.2)); }
導航條、氣泡都完成了,接下來就非常簡單了,內容的填充我們就不能用ListView了,這里我們需要一個官方的插件: scrollable_positioned_list: ^0.2.3
使用它可以定位到具體的item位置,這樣我們就可以進行列表定位和導航條進行聯動了。
內容代碼:
ScrollablePositionedList.builder( physics: BouncingScrollPhysics(), itemScrollController: _scrollController, itemBuilder: (context, index) { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ Container( padding: EdgeInsetsDirectional.only(start: 10), child: Text(dataList[index].initial ?? ""), color: Colors.grey.shade300, height: 30, width: double.infinity, alignment: Alignment.centerLeft, ), Container( padding: EdgeInsetsDirectional.only(start: 15), child: ListView.builder( // 禁用滑動事件 physics: NeverScrollableScrollPhysics(), shrinkWrap: true, itemBuilder: (context, cityIndex) { return InkWell( child: Container( height: 40, child: Column( children: [ Expanded( child: Container( child: Text(dataList[index] .nameList?[cityIndex] .name ?? ""), alignment: Alignment.centerLeft, ), ), Divider( height: 0.5, ) ], ), ), onTap: () {}, ); }, itemCount: dataList[index].nameList?.length, ), ) ], ); }, itemCount: dataList.length, )
ScrollablePositionedList用法基本和ListView一直,只是它多了一個這個方法,可以定位到具體的item。
_scrollController.jumpTo(index: i);
看下最終效果:請忽略數據的重復...手動填充數據太麻煩了,有哪里不懂可以交流哦
總結:
通過這個組件我們可以簡單的了解Flutter的手勢交互操作,通過手勢識別我們可以實現很多有意思的組件,尤其結合繪制和動畫可以做出來非常有意思的交互,所有的交互最終的底層都是通過手勢識別完成的,以后有時間研究下源碼再和大家分享~
原文鏈接:https://juejin.cn/post/7083806995085525006
相關推薦
- 2022-08-07 API處理Android安全距離詳情_Android
- 2022-12-12 C語言使用函數實現字符串部分復制問題_C 語言
- 2022-03-20 C++字符串和數字的去重操作和鞍點的尋找_C 語言
- 2022-10-01 Golang?中的?unsafe.Pointer?和?uintptr詳解_Golang
- 2022-12-05 Python異常?ValueError的問題_python
- 2023-07-05 uni-app處理請求發送表單類型的數據
- 2022-03-31 python中常用的九個語法技巧_python
- 2022-02-18 RuntimeError: CUDA error: invalid device ordinal
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支