Flutter實(shí)現(xiàn)自定義下拉選擇框的示例詳解
在一些列表頁(yè)面中,我們經(jīng)常會(huì)有上方篩選項(xiàng)的的需求,點(diǎn)擊出現(xiàn)一個(gè)下拉菜單,多選、單選、列表選等,而在Flutter中,并沒(méi)有現(xiàn)成的這樣的組件,找第三方的擴(kuò)展有時(shí)候又會(huì)受到一定限制,所以最好我們可以自己做一個(gè),這樣即使擴(kuò)展我們也會(huì)得心應(yīng)手。
先看效果圖:
關(guān)鍵點(diǎn):彈出、收回動(dòng)畫(huà)、狀態(tài)改變、選項(xiàng)聯(lián)動(dòng)
思路: 我們可以看到一個(gè)完整的下拉框有頭部和具體的下拉選項(xiàng)兩部分組成,頭部又和下拉組進(jìn)行了聯(lián)動(dòng), 把頭部當(dāng)做1個(gè)數(shù)組,下方選項(xiàng)作為1個(gè)數(shù)組,兩個(gè)數(shù)組數(shù)量一致之間形成一個(gè)完整的下拉選擇框可以更好的控制聯(lián)動(dòng)效果。
首先我們看彈出和收回,我們可以把他看作一個(gè)組件的高度由0逐漸擴(kuò)大再逐漸縮小至0,只要我們對(duì)這個(gè)組件的高度用動(dòng)畫(huà)來(lái)進(jìn)行控制就可以實(shí)現(xiàn),下方有一個(gè)黑色透明度漸變,這里我們根據(jù)上方彈窗的動(dòng)畫(huà)來(lái)改變下方黑色陰影的變化即可。
關(guān)鍵代碼:
/// 下拉組件 @override Widget build(BuildContext context) { return Column( children: [ _MenuBuilder( animation: animation, // 這里顯示我們需要的具體下拉框選項(xiàng)內(nèi)容 child: widget.children[widget.menuController.index], ), isShowShadow // 是否顯示下方黑色陰影 只有下拉彈出才顯示 這個(gè)地方我們就可以根據(jù)UI設(shè)計(jì)來(lái)進(jìn)行高度自定義 ? Expanded( child: InkWell( child: AnimatedBuilder( animation: animation, builder: (context, child) { // 這里是下拉框下方陰影 點(diǎn)擊陰影隱藏下拉框 return Container( width: double.infinity, height: MediaQuery.of(context).size.height, color: Colors.black .withOpacity(animation.value / (widget.height * 3)), ); }), onTap: () { widget.menuController.hide(); }, )) : const SizedBox(), ], ); } class _MenuBuilder extends StatelessWidget { final Animation<double> animation; final Widget child; const _MenuBuilder({required this.animation, required this.child}); // 這里我們主要用動(dòng)畫(huà)來(lái)控制下拉內(nèi)容組件的高度 @override Widget build(BuildContext context) { return AnimatedBuilder( child: child, animation: animation, builder: (context, child) { return Container( color: Colors.white, height: animation.value, child: child, ); }); }
接下來(lái)我們看頭部設(shè)計(jì),頭部設(shè)計(jì)稍微復(fù)雜一些,主要還是狀態(tài)的改變,選項(xiàng)之間的聯(lián)動(dòng),這里我們新建一個(gè)狀態(tài)控制器:主要來(lái)對(duì)頭部的一些狀態(tài)進(jìn)行控制,比如點(diǎn)擊頭部按鈕之后的文字or顏色的改變,選擇完畢顏色的保存,重置顏色的恢復(fù)等一些狀態(tài),下方控制器主要就是來(lái)控制頭部的一些狀態(tài)。
/// 菜單控制器 class MenuController extends ChangeNotifier { // 當(dāng)前組件是否顯示 默認(rèn)不顯示 針對(duì)整個(gè)菜單數(shù)組 bool isShow = false; // 顯示當(dāng)前組件title的文本 共用 String title = ""; // 顯示哪個(gè)下拉框 int index = 0; // 選擇下拉框的的title 這個(gè)字段只有在真正選擇的時(shí)候才會(huì)改變 int titleIndex = 0; /// 更改Title changeTitle(int titleIndex, String? title) { this.titleIndex = titleIndex; this.title = title ?? ""; hide(); } // 顯示下拉 index 為下拉哪一個(gè)菜單的index show(int index) { this.index = index; if (!isShow) { isShow = true; } notifyListeners(); } // 隱藏 取消 hide() { isShow = false; notifyListeners(); } }
有了控制器我們還需要對(duì)頭部數(shù)據(jù)進(jìn)行處理,首先我們的頭部在沒(méi)有選擇選項(xiàng)的時(shí)候會(huì)有一個(gè)默認(rèn)的數(shù)組,這個(gè)是永遠(yuǎn)不會(huì)改變的,所以這個(gè)數(shù)組一旦設(shè)置了就不能改變了,之后我們新建一個(gè)動(dòng)態(tài)數(shù)組也就是當(dāng)前顯示的數(shù)組,這個(gè)數(shù)組的默認(rèn)值就是我們未選擇選項(xiàng)的默認(rèn)值,這里我們需要監(jiān)聽(tīng)頭部狀態(tài)的改變來(lái)對(duì)顯示數(shù)組進(jìn)行處理。
關(guān)鍵代碼:重點(diǎn) 主要針對(duì)頭部狀態(tài)改變的處理,這塊代碼搞清楚了,基本就OK了。
@override void initState() { super.initState(); // changeTitles就是我們的顯示數(shù)組 changeTitles.addAll(widget.titles); for (var i = 0; i < changeTitles.length; i++) { //_chindren 是我們的頭部組件數(shù)組 _children.add(searchFilter(changeTitles[i], i)); } widget.menuController.addListener(() { // 下拉 true 隱藏 false var isShow = widget.menuController.isShow; // 改變頭部狀態(tài) setState(() { if (widget.menuController.title != "") { // 說(shuō)明當(dāng)前選擇了選項(xiàng) 賦值我選擇的選項(xiàng) changeTitles[widget.menuController.titleIndex] = widget.menuController.title; } else { // 為空 說(shuō)明當(dāng)前的選項(xiàng)我清空了 賦值初始頭部數(shù)組的數(shù)據(jù) changeTitles[widget.menuController.titleIndex] = widget.titles[widget.menuController.titleIndex]; } // currentIndex 當(dāng)前選擇的index 默認(rèn)-1 用來(lái)對(duì)比更新頭部文字和顏色 // 如果下拉 更新當(dāng)前選項(xiàng)inedx 如果隱藏說(shuō)明沒(méi)有選擇任何一個(gè)下拉框 置為-1 if (isShow && currentIndex < widget.titles.length) { currentIndex = widget.menuController.index; } else { currentIndex = -1; } // 每次下拉收回我們只需改變頭部數(shù)據(jù)即可 changeTitles 永遠(yuǎn)都是顯示的數(shù)組 直接全部更新到組件即可 _children.clear(); for (var i = 0; i < changeTitles.length; i++) { _children.add(searchFilter(changeTitles[i], i)); } }); }); } // 這里就是一個(gè)簡(jiǎn)單的Row數(shù)組 按照百分比排列 也可以自定義不同寬度 @override Widget build(BuildContext context) { return SizedBox( height: widget.headHeight ?? 45, child: Row(children: _children), ); }
主要對(duì)頭部文本內(nèi)容以及顏色進(jìn)行更新,如果當(dāng)前選項(xiàng)=頭部中的選項(xiàng)||或者 選項(xiàng)賦值的名字不等于初始值我們就認(rèn)為選中了此菜單,從而改變顏色。到這里基本邏輯就梳理清楚了,下拉框樣式這個(gè)可以自己根據(jù)自己的業(yè)務(wù)進(jìn)行自定義。
Widget searchFilter(String name, int index) { TextStyle(color: currentIndex == index || widget.titles[index] != name ? widget.clickColor : widget.defaultColor), }
總結(jié)
思路就是整個(gè)頁(yè)面的下拉組件視為一體,這樣做的目的也是為了在切換不同選項(xiàng)的時(shí)候可以省略收回又打開(kāi)的繁瑣動(dòng)畫(huà),可以有一種整體的體驗(yàn),那在做這個(gè)下拉框主要用到了動(dòng)畫(huà)以及狀態(tài)管理相關(guān)的技能,可以說(shuō)在Flutter編程思想里,狀態(tài)式編程是跟原生的應(yīng)用式編程的最大區(qū)別,只有真正的掌握了這兩個(gè)技能,才能更好的理解下拉框的實(shí)現(xiàn)的過(guò)程。
到此這篇關(guān)于Flutter實(shí)現(xiàn)自定義下拉選擇框的示例詳解的文章就介紹到這了,更多相關(guān)Flutter下拉選擇框內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android開(kāi)發(fā)中setContentView和inflate的區(qū)別分析
這篇文章主要介紹了Android開(kāi)發(fā)中setContentView和inflate的區(qū)別,較為詳細(xì)的分析了setContentView和inflate的功能、用法及二者的區(qū)別,需要的朋友可以參考下2016-07-07Android 微信小視頻錄制功能實(shí)現(xiàn)詳細(xì)介紹
這篇文章主要介紹了Android 微信小視頻錄制功能實(shí)現(xiàn)詳解的相關(guān)資料,這里提供了具體的實(shí)現(xiàn)思路及代碼,需要的朋友可以參考下2016-11-11Flutter 用自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)實(shí)現(xiàn)頁(yè)面切換
本篇介紹了 fluro 導(dǎo)航到其他頁(yè)面的自定義轉(zhuǎn)場(chǎng)動(dòng)畫(huà)實(shí)現(xiàn),F(xiàn)lutter本身提供了不少預(yù)定義的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),可以通過(guò) transitionBuilder 參數(shù)設(shè)計(jì)多種多樣的轉(zhuǎn)場(chǎng)動(dòng)畫(huà),也可以通過(guò)自定義的 AnimatedWidget實(shí)現(xiàn)個(gè)性化的轉(zhuǎn)場(chǎng)動(dòng)畫(huà)效果。2021-06-06Android主項(xiàng)目與Module中R類(lèi)的區(qū)別詳解
這篇文章主要給大家介紹了關(guān)于Android主項(xiàng)目與Module中R類(lèi)的區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-02-02Flutter利用SizeTransition實(shí)現(xiàn)組件飛入效果
本文將為大家介紹SizeTransition,SizeTransition用于更改子組件的尺寸來(lái)實(shí)現(xiàn)動(dòng)畫(huà),支持垂直方向或水平方向修改動(dòng)畫(huà)。本文將利用其實(shí)現(xiàn)組件飛入效果,需要的可以參考一下2022-04-04Android中App字體大小不隨系統(tǒng)改變而改變
這篇文章主要介紹了Android中App字體大小不隨系統(tǒng)改變而改變,需要的朋友可以參考下2017-04-04Android調(diào)用系統(tǒng)照相機(jī)拍照與攝像的方法
這篇文章主要為大家詳細(xì)介紹了Android如何調(diào)用系統(tǒng)現(xiàn)有的照相機(jī)拍照與攝像,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-04-04