怎么使用Flutter刷新组件RefreshIndicator自定义样式demo(demo,flutter,开发技术)

时间:2024-05-09 15:21:53 作者 : 石家庄SEO 分类 : 开发技术
  • TAG :

前言

RefreshIndicator是Flutter里常见的下拉刷新组件,使用是比较方便的。但由于产品兄弟对其固定的刷新样式很是不满,而且代码中已经引入了很多RefreshIndicator,直接替换其他组件的话,对代码的改动可能比较大,所以只能自己动手改一改源码,在达到产品的要求的同时尽可能减少代码的修改。

简单的样式修改

简单的样式修改,如想换成顺时针旋转的 iOS 风格活动指示器,只需替换对应样式代码即可。查看RefreshIndicator的源码,代码翻到最下面就可以看到其实是自定义了一个RefreshProgressIndicator样式,通过继承CircularProgressIndicator来实现初始样式。

怎么使用Flutter刷新组件RefreshIndicator自定义样式demo

所以我们只需简单的替换掉该样式即可实现简单的样式修改。

AnimatedBuilder(animation:_positionController,builder:(BuildContextcontext,Widget?child){returnClipOval(child:Container(padding:constEdgeInsets.all(10),decoration:BoxDecoration(color:widget.backgroundColor??Colors.white),child:CupertinoActivityIndicator(color:widget.color)),);},)

如此便可实现简单的样式修改。

复杂的样式修改

简单的样式修改只是换换样式,对刷新动作本身是没有任何修改的,也就是刷新操作样式本身没有变,只是换了个皮。而国内的刷新操作样式基本是上图效果3,所以如果要在RefreshIndicator上修改成效果3,除了要将原有样式Stack改为Column外,还需要自己处理手势,这里可以使用Listener来操作手势。

代码如下,修改的地方都有注释。

//Copyright2014TheFlutterAuthors.Allrightsreserved.//UseofthissourcecodeisgovernedbyaBSD-stylelicensethatcanbe//foundintheLICENSEfile.import'dart:async';import'dart:math'asmath;import'dart:ui';import'package:flutter/cupertino.dart';import'package:flutter/material.dart';import'package:get/get.dart';//Theover-scrolldistancethatmovestheindicatortoitsmaximum//displacement,asapercentageofthescrollable'scontainerextent.constdouble_kDragContainerExtentPercentage=0.25;//Howmuchthescroll'sdraggesturecanovershoottheRefreshIndicator's//displacement;maxdisplacement=_kDragSizeFactorLimit*displacement.constdouble_kDragSizeFactorLimit=1.5;//Whenthescrollends,thedurationoftherefreshindicator'sanimation//totheRefreshIndicator'sdisplacement.constDuration_kIndicatorSnapDuration=Duration(milliseconds:150);//ThedurationoftheScaleTransitionthatstartswhentherefreshaction//hascompleted.constDuration_kIndicatorScaleDuration=Duration(milliseconds:200);///Thesignatureforafunctionthat'scalledwhentheuserhasdraggeda///[RefreshIndicator]farenoughtodemonstratethattheywanttheappto///refresh.Thereturned[Future]mustcompletewhentherefreshoperationis///finished.//////Usedby[RefreshIndicator.onRefresh].typedefRefreshCallback=Future<void>Function();//Thestatemachinemovesthroughthesemodesonlywhenthescrollable//identifiedbyscrollableKeyhasbeenscrolledtoitsminormaxlimit.enum_RefreshIndicatorMode{drag,//Pointerisdown.armed,//DraggedfarenoughthatanupeventwillruntheonRefreshcallback.snap,//Animatingtotheindicator'sfinal"displacement".refresh,//Runningtherefreshcallback.done,//Animatingtheindicator'sfade-outafterrefreshing.canceled,//Animatingtheindicator'sfade-outafternotarming.}///Usedtoconfigurehow[RefreshIndicator]canbetriggered.enumRefreshIndicatorTriggerMode{///Theindicatorcanbetriggeredregardlessofthescrollposition///ofthe[Scrollable]whenthedragstarts.anywhere,///Theindicatorcanonlybetriggeredifthe[Scrollable]isattheedge///whenthedragstarts.onEdge,}///AwidgetthatsupportstheMaterial"swipetorefresh"idiom.//////{@youtube560315https://www.youtube.com/watch?v=ORApMlzwMdM}//////Whenthechild's[Scrollable]descendantoverscrolls,ananimatedcircular///progressindicatorisfadedintoview.Whenthescrollends,ifthe///indicatorhasbeendraggedfarenoughforittobecomecompletelyopaque,///the[onRefresh]callbackiscalled.Thecallbackisexpectedtoupdatethe///scrollable'scontentsandthencompletethe[Future]itreturns.Therefresh///indicatordisappearsafterthecallback's[Future]hascompleted.//////Thetriggermodeisconfiguredby[RefreshIndicator.triggerMode].//////{@tooldartpad}///Thisexampleshowshow[RefreshIndicator]canbetriggeredindifferentways.//////**Seecodeinexamples/api/lib/material/refresh_indicator/refresh_indicator.0.dart**///{@end-tool}//////##Troubleshooting//////###Refreshindicatordoesnotshowup//////The[RefreshIndicator]willappearifitsscrollabledescendantcanbe///overscrolled,i.e.ifthescrollable'scontentisbiggerthanitsviewport.///Toensurethatthe[RefreshIndicator]willalwaysappear,evenifthe///scrollable'scontentfitswithinitsviewport,setthescrollable's///[Scrollable.physics]propertyto[AlwaysScrollableScrollPhysics]://////```dart///ListView(///physics:constAlwaysScrollableScrollPhysics(),///children:...///)///```//////A[RefreshIndicator]canonlybeusedwithaverticalscrollview.//////Seealso://////*<https://material.io/design/platform-guidance/android-swipe-to-refresh.html>///*[RefreshIndicatorState],canbeusedtoprogrammaticallyshowtherefreshindicator.///*[RefreshProgressIndicator],widgetusedby[RefreshIndicator]toshow///theinnercircularprogressspinnerduringrefreshes.///*[CupertinoSliverRefreshControl],aniOSequivalentofthepull-to-refreshpattern.///Mustbeusedasasliverinsidea[CustomScrollView]insteadofwrapping///arounda[ScrollView]becauseit'sapartofthescrollableinsteadof///beingoverlaidontopofit.classRefreshIndicatorNeoextendsStatefulWidget{///Createsarefreshindicator.//////The[onRefresh],[child],and[notificationPredicate]argumentsmustbe///non-null.Thedefault///[displacement]is40.0logicalpixels.//////The[semanticsLabel]isusedtospecifyanaccessibilitylabelforthiswidget.///Ifitisnull,itwillbedefaultedto[MaterialLocalizations.refreshIndicatorSemanticLabel].///Anemptystringmaybepassedtoavoidhavinganythingreadbyscreenreadingsoftware.///The[semanticsValue]maybeusedtospecifyprogressonthewidget.constRefreshIndicatorNeo({Key?key,requiredthis.child,this.displacement=40.0,this.edgeOffset=0.0,requiredthis.onRefresh,this.color,this.backgroundColor,this.notificationPredicate=defaultScrollNotificationPredicate,this.semanticsLabel,this.semanticsValue,this.strokeWidth=RefreshProgressIndicator.defaultStrokeWidth,this.triggerMode=RefreshIndicatorTriggerMode.onEdge,}):assert(child!=null),assert(onRefresh!=null),assert(notificationPredicate!=null),assert(strokeWidth!=null),assert(triggerMode!=null),super(key:key);///Thewidgetbelowthiswidgetinthetree.//////Therefreshindicatorwillbestackedontopofthischild.Theindicator///willappearwhenchild'sScrollabledescendantisover-scrolled.//////Typicallya[ListView]or[CustomScrollView].finalWidgetchild;///Thedistancefromthechild'stoporbottom[edgeOffset]where///therefreshindicatorwillsettle.Duringthedragthatexposestherefresh///indicator,itsactualdisplacementmaysignificantlyexceedthisvalue.//////Inmostcases,[displacement]distancestartscountingfromtheparent's///edges.However,if[edgeOffset]islargerthanzerothenthe[displacement]///valueiscalculatedfromthatoffsetinsteadoftheparent'sedge.finaldoubledisplacement;///Theoffsetwhere[RefreshProgressIndicator]startstoappearondragstart.//////Dependingwhethertheindicatorisshowingonthetoporbottom,thevalue///ofthisvariablecontrolshowfarfromtheparent'sedgetheprogress///indicatorstartstoappear.Thismaycomeinhandywhen,forexample,the///UIcontainsatop[Widget]whichcoverstheparent'sedgewheretheprogress///indicatorwouldotherwiseappear.//////Bydefault,theedgeoffsetissetto0.//////Seealso://////*[displacement],canbeusedtochangethedistancefromtheedgethat///theindicatorsettles.finaldoubleedgeOffset;///Afunctionthat'scalledwhentheuserhasdraggedtherefreshindicator///farenoughtodemonstratethattheywanttheapptorefresh.Thereturned///[Future]mustcompletewhentherefreshoperationisfinished.finalRefreshCallbackonRefresh;///Theprogressindicator'sforegroundcolor.Thecurrenttheme's///[ColorScheme.primary]bydefault.finalColor?color;///Theprogressindicator'sbackgroundcolor.Thecurrenttheme's///[ThemeData.canvasColor]bydefault.finalColor?backgroundColor;///Acheckthatspecifieswhethera[ScrollNotification]shouldbe///handledbythiswidget.//////Bydefault,checkswhether`notification.depth==0`.Setittosomething///elseformorecomplicatedlayouts.finalScrollNotificationPredicatenotificationPredicate;///{@macroflutter.progress_indicator.ProgressIndicator.semanticsLabel}//////Thiswillbedefaultedto[MaterialLocalizations.refreshIndicatorSemanticLabel]///ifitisnull.finalString?semanticsLabel;///{@macroflutter.progress_indicator.ProgressIndicator.semanticsValue}finalString?semanticsValue;///Defines`strokeWidth`for`RefreshIndicator`.//////Bydefault,thevalueof`strokeWidth`is2.0pixels.finaldoublestrokeWidth;///Defineshowthis[RefreshIndicator]canbetriggeredwhenusersoverscroll.//////The[RefreshIndicator]canbepulledoutintwocases,///1,Keepdraggingifthescrollablewidgetattheedgewithzeroscrollposition///whenthedragstarts.///2,Keepdraggingafteroverscrolloccursifthescrollablewidgethas///anon-zeroscrollpositionwhenthedragstarts.//////Ifthisis[RefreshIndicatorTriggerMode.anywhere],bothofthecasesabovecanbetriggered.//////Ifthisis[RefreshIndicatorTriggerMode.onEdge],onlycase1canbetriggered.//////Defaultsto[RefreshIndicatorTriggerMode.onEdge].finalRefreshIndicatorTriggerModetriggerMode;@overrideRefreshIndicatorNeoStatecreateState()=>RefreshIndicatorNeoState();}///Containsthestatefora[RefreshIndicator].Thisclasscanbeusedto///programmaticallyshowtherefreshindicator,seethe[show]method.classRefreshIndicatorNeoStateextendsState<RefreshIndicatorNeo>withTickerProviderStateMixin<RefreshIndicatorNeo>{lateAnimationController_positionController;lateAnimationController_scaleController;lateAnimation<double>_positionFactor;lateAnimation<double>_scaleFactor;lateAnimation<double>_value;lateAnimation<Color?>_valueColor;_RefreshIndicatorMode?_mode;lateFuture<void>_pendingRefreshFuture;bool?_isIndicatorAtTop;double?_dragOffset;staticfinalAnimatable<double>_threeQuarterTween=Tween<double>(begin:0.0,end:0.75);staticfinalAnimatable<double>_kDragSizeFactorLimitTween=Tween<double>(begin:0.0,end:_kDragSizeFactorLimit);staticfinalAnimatable<double>_oneToZeroTween=Tween<double>(begin:1.0,end:0.0);@overridevoidinitState(){super.initState();_positionController=AnimationController(vsync:this);_positionFactor=_positionController.drive(_kDragSizeFactorLimitTween);_value=_positionController.drive(_threeQuarterTween);//The"value"ofthecircularprogressindicatorduringadrag._scaleController=AnimationController(vsync:this);_scaleFactor=_scaleController.drive(_oneToZeroTween);}@overridevoiddidChangeDependencies(){finalThemeDatatheme=Theme.of(context);_valueColor=_positionController.drive(ColorTween(begin:(widget.color??theme.colorScheme.primary).withOpacity(0.0),end:(widget.color??theme.colorScheme.primary).withOpacity(1.0),).chain(CurveTween(curve:constInterval(0.0,1.0/_kDragSizeFactorLimit),)),);super.didChangeDependencies();}@overridevoiddidUpdateWidget(covariantRefreshIndicatorNeooldWidget){super.didUpdateWidget(oldWidget);if(oldWidget.color!=widget.color){finalThemeDatatheme=Theme.of(context);_valueColor=_positionController.drive(ColorTween(begin:(widget.color??theme.colorScheme.primary).withOpacity(0.0),end:(widget.color??theme.colorScheme.primary).withOpacity(1.0),).chain(CurveTween(curve:constInterval(0.0,1.0/_kDragSizeFactorLimit),)),);}}@overridevoiddispose(){_positionController.dispose();_scaleController.dispose();super.dispose();}bool_shouldStart(ScrollNotificationnotification){//Ifthenotification.dragDetailsisnull,thisscrollisnottriggeredby//userdragging.ItmaybearesultofScrollController.jumpToorballisticscroll.//Inthiscase,wedon'twanttotriggertherefreshindicator.return((notificationisScrollStartNotification&&notification.dragDetails!=null)||(notificationisScrollUpdateNotification&&notification.dragDetails!=null&&widget.triggerMode==RefreshIndicatorTriggerMode.anywhere))&&((notification.metrics.axisDirection==AxisDirection.up&&notification.metrics.extentAfter==0.0)||(notification.metrics.axisDirection==AxisDirection.down&&notification.metrics.extentBefore==0.0))&&_mode==null&&_start(notification.metrics.axisDirection);}bool_handleScrollNotification(ScrollNotificationnotification){if(!widget.notificationPredicate(notification))returnfalse;if(_shouldStart(notification)){setState((){_mode=_RefreshIndicatorMode.drag;});returnfalse;}bool?indicatorAtTopNow;switch(notification.metrics.axisDirection){caseAxisDirection.down:caseAxisDirection.up:indicatorAtTopNow=true;break;caseAxisDirection.left:caseAxisDirection.right:indicatorAtTopNow=true;break;}if(indicatorAtTopNow!=_isIndicatorAtTop){if(_mode==_RefreshIndicatorMode.drag||_mode==_RefreshIndicatorMode.armed)_dismiss(_RefreshIndicatorMode.canceled);}elseif(notificationisScrollUpdateNotification){if(_mode==_RefreshIndicatorMode.drag||_mode==_RefreshIndicatorMode.armed){if((notification.metrics.axisDirection==AxisDirection.down&&notification.metrics.extentBefore>0.0)||(notification.metrics.axisDirection==AxisDirection.up&&notification.metrics.extentAfter>0.0)){_dismiss(_RefreshIndicatorMode.canceled);}else{if(notification.metrics.axisDirection==AxisDirection.down){_dragOffset=_dragOffset!-notification.scrollDelta!;}elseif(notification.metrics.axisDirection==AxisDirection.up){_dragOffset=_dragOffset!+notification.scrollDelta!;}_checkDragOffset(notification.metrics.viewportDimension);}}if(_mode==_RefreshIndicatorMode.armed&&notification.dragDetails==null){//OniOSstarttherefreshwhentheScrollablebouncesbackfromthe//overscroll(ScrollNotificationindicatingthisdon'thavedragDetails//becausethescrollactivityisnotdirectlytriggeredbyadrag)._show();}}elseif(notificationisOverscrollNotification){if(_mode==_RefreshIndicatorMode.drag||_mode==_RefreshIndicatorMode.armed){if(notification.metrics.axisDirection==AxisDirection.down){_dragOffset=_dragOffset!-notification.overscroll;}elseif(notification.metrics.axisDirection==AxisDirection.up){_dragOffset=_dragOffset!+notification.overscroll;}_checkDragOffset(notification.metrics.viewportDimension,needIntercept:true);}}elseif(notificationisScrollEndNotification){switch(_mode){case_RefreshIndicatorMode.armed:_show();break;case_RefreshIndicatorMode.drag:_dismiss(_RefreshIndicatorMode.canceled);break;case_RefreshIndicatorMode.canceled:case_RefreshIndicatorMode.done:case_RefreshIndicatorMode.refresh:case_RefreshIndicatorMode.snap:casenull://donothingbreak;}}returnfalse;}bool_handleGlowNotification(OverscrollIndicatorNotificationnotification){if(notification.depth!=0||!notification.leading)returnfalse;if(_mode==_RefreshIndicatorMode.drag){notification.disallowGlow();returntrue;}returnfalse;}bool_start(AxisDirectiondirection){assert(_mode==null);assert(_isIndicatorAtTop==null);assert(_dragOffset==null);switch(direction){caseAxisDirection.down:caseAxisDirection.up:_isIndicatorAtTop=true;break;caseAxisDirection.left:caseAxisDirection.right:_isIndicatorAtTop=null;//wedonotsupporthorizontalscrollviews.returnfalse;}_dragOffset=0.0;_scaleController.value=0.0;_positionController.value=0.0;returntrue;}void_checkDragOffset(doublecontainerExtent,{boolneedIntercept=true}){if(needIntercept){assert(_mode==_RefreshIndicatorMode.drag||_mode==_RefreshIndicatorMode.armed);}doublenewValue=_dragOffset!/(containerExtent*_kDragContainerExtentPercentage);if(_mode==_RefreshIndicatorMode.armed){newValue=math.max(newValue,1.0/_kDragSizeFactorLimit);}_positionController.value=newValue.clamp(0.0,1.0);//thistriggersvariousrebuildsif(_mode==_RefreshIndicatorMode.drag&&_valueColor.value!.alpha==0xFF){_mode=_RefreshIndicatorMode.armed;}}//Stopshowingtherefreshindicator.Future<void>_dismiss(_RefreshIndicatorModenewMode,{Duration?time})async{awaitFuture<void>.value();//Thiscanonlybecalledfrom_show()whenrefreshingand//_handleScrollNotificationinresponsetoaScrollEndNotificationor//directionchange.assert(newMode==_RefreshIndicatorMode.canceled||newMode==_RefreshIndicatorMode.done);setState((){_mode=newMode;});switch(_mode!){//注释:刷新结束,关闭动画case_RefreshIndicatorMode.done:_scaleController.animateTo(1.0,duration:time??_kIndicatorScaleDuration).whenComplete((){});_doneAnimation=Tween<double>(begin:getPos(pos.value),end:0).animate(_scaleController);if(_doneAnimation!=null){_doneAnimation?.addListener((){//赋值高度pos(_doneAnimation?.value??0);if((_doneAnimation?.value??0)==0){_doneAnimation=null;}});}break;case_RefreshIndicatorMode.canceled:await_positionController.animateTo(0.0,duration:time??_kIndicatorScaleDuration);break;case_RefreshIndicatorMode.armed:case_RefreshIndicatorMode.drag:case_RefreshIndicatorMode.refresh:case_RefreshIndicatorMode.snap:assert(false);}if(mounted&&_mode==newMode){_dragOffset=null;_isIndicatorAtTop=null;setState((){_mode=null;});}}void_show(){assert(_mode!=_RefreshIndicatorMode.refresh);assert(_mode!=_RefreshIndicatorMode.snap);//finalCompleter<void>completer=Completer<void>();//_pendingRefreshFuture=completer.future;_mode=_RefreshIndicatorMode.snap;_positionController.animateTo(1.0/_kDragSizeFactorLimit,duration:_kIndicatorSnapDuration).then<void>((voidvalue){if(mounted&&_mode==_RefreshIndicatorMode.snap){assert(widget.onRefresh!=null);setState((){//Showtheindeterminateprogressindicator._mode=_RefreshIndicatorMode.refresh;});//注释:删掉这段代码,因为需要跟随手势,在手势释放的时候才执行,见下方手势控制onPointerUp//finalFuture<void>refreshResult=widget.onRefresh();//assert((){//if(refreshResult==null)//FlutterError.reportError(FlutterErrorDetails(//exception:FlutterError(//'TheonRefreshcallbackreturnednull.\n'//'TheRefreshIndicatoronRefreshcallbackmustreturnaFuture.',//),//context:ErrorDescription('whencallingonRefresh'),//library:'materiallibrary',//));//returntrue;//}());//if(refreshResult==null)return;//refreshResult.whenComplete((){//if(mounted&&_mode==_RefreshIndicatorMode.refresh){//completer.complete();//_dismiss(_RefreshIndicatorMode.done);//}//});}});}///Showtherefreshindicatorandruntherefreshcallbackasifithad///beenstartedinteractively.Ifthismethodiscalledwhiletherefresh///callbackisrunning,itquietlydoesnothing.//////Creatingthe[RefreshIndicator]witha[GlobalKey<RefreshIndicatorState>]///makesitpossibletorefertothe[RefreshIndicatorState].//////Thefuturereturnedfromthismethodcompleteswhenthe///[RefreshIndicator.onRefresh]callback'sfuturecompletes.//////Ifyouawaitthefuturereturnedbythisfunctionfroma[State],you///shouldcheckthatthestateisstill[mounted]beforecalling[setState].//////Wheninitiatedinthismanner,therefreshindicatorisindependentofany///actualscrollview.Itdefaultstoshowingtheindicatoratthetop.To///showitatthebottom,set`atTop`tofalse.Future<void>show({boolatTop=true}){if(_mode!=_RefreshIndicatorMode.refresh&&_mode!=_RefreshIndicatorMode.snap){if(_mode==null)_start(atTop?AxisDirection.down:AxisDirection.up);_show();}return_pendingRefreshFuture;}//点击时的Ydouble_downY=0.0;//最后的移动Ydouble_lastMoveY=0.0;//手势移动距离,对应下拉效果的位移//因为需要制造弹性效果,调用getPos()模拟弹性RxDoublepos=0.0.obs;//手势状态MoveTypemoveType=MoveType.UP;finaldoublebottomImg=10;//手势下拉动画,主要对pos赋值lateAnimation<double>?_animation;//结束动画,主要对pos重新赋值至0lateAnimation<double>?_doneAnimation;lateAnimationController_controller;///模拟下拉的弹性doublegetPos(doublepos){if(pos<=0){return0;}elseif(pos<100){returnpos*0.7;}elseif(pos<200){return70+((pos-100)*0.5);}elseif(pos<300){return120+((pos-200)*0.3);}else{return150+((pos-300)*0.1);}}@overrideWidgetbuild(BuildContextcontext){assert(debugCheckHasMaterialLocalizations(context));finalWidgetchild=NotificationListener<ScrollNotification>(onNotification:_handleScrollNotification,child:widget.child,//NotificationListener<OverscrollIndicatorNotification>(////onNotification:_handleGlowNotification,//child:widget.child,//),);assert((){if(_mode==null){assert(_dragOffset==null);assert(_isIndicatorAtTop==null);}else{assert(_dragOffset!=null);assert(_isIndicatorAtTop!=null);}returntrue;}());finalboolshowIndeterminateIndicator=_mode==_RefreshIndicatorMode.refresh||_mode==_RefreshIndicatorMode.done;doubleimgHeight=MediaQueryData.fromWindow(window).size.width/7;doubleimgAllHeight=imgHeight+bottomImg;returnListener(onPointerDown:(PointerDownEventevent){//手指按下的距离_downY=event.position.distance;moveType=MoveType.DOWN;},onPointerMove:(PointerMoveEventevent){if(moveType!=MoveType.MOVE||_mode==null){setState((){moveType=MoveType.MOVE;});}moveType=MoveType.MOVE;//手指移动的距离varposition=event.position.distance;//判断距离差vardetal=position-_lastMoveY;///到达顶部才计算if(_isIndicatorAtTop!=null&&_isIndicatorAtTop!&&_mode!=null){pos(position-_downY);if(detal>0){//================向下移动================}else{//================向上移动================///当刷新动画执行时,手指上滑就直接取消刷新动画if(_mode==_RefreshIndicatorMode.refresh&&pos.value!=0){_dismiss(_RefreshIndicatorMode.canceled,time:Duration(microseconds:500));}}}_lastMoveY=position;},onPointerUp:(PointerUpEventevent){if(_isIndicatorAtTop!=null&&_isIndicatorAtTop!){doubleheightPos=pos.value;doubleimgHeight=0;///计算图片高度,因为最终转成pos,因为pos被转换过getPos()//所以反转的时候需要再次计算if(imgAllHeight<100){imgHeight=imgAllHeight/0.7;}elseif(imgAllHeight<200){imgHeight=(imgAllHeight-20)/0.5;}elseif(imgAllHeight<300){imgHeight=(imgAllHeight-60)/0.3;}//松手后的回弹效果_controller=AnimationController(vsync:this,duration:Duration(milliseconds:250),)..forward().whenComplete((){///动画结束后触发onRefresh()方法if(_mode==_RefreshIndicatorMode.refresh){finalCompleter<void>completer=Completer<void>();_pendingRefreshFuture=completer.future;finalFuture<void>refreshResult=widget.onRefresh();assert((){if(refreshResult==null){FlutterError.reportError(FlutterErrorDetails(exception:FlutterError('TheonRefreshcallbackreturnednull.\n''TheRefreshIndicatoronRefreshcallbackmustreturnaFuture.',),context:ErrorDescription('whencallingonRefresh'),library:'materiallibrary',));}returntrue;}());if(refreshResult==null)return;refreshResult.whenComplete((){if(mounted&&_mode==_RefreshIndicatorMode.refresh){completer.complete();///onRefresh()执行完后关闭动画_dismiss(_RefreshIndicatorMode.done);}});}});_animation=Tween<double>(begin:heightPos,end:imgHeight).animate(_controller);_animation?.addListener((){//下拉动画变化,赋值高度if(_mode==_RefreshIndicatorMode.refresh){pos(_animation?.value??0);if(_animation?.value==imgHeight){_animation=null;}}});}moveType=MoveType.UP;},child:Obx(()=>Column(children:[if(_isIndicatorAtTop!=null&&_isIndicatorAtTop!&&_mode!=null&&moveType==MoveType.MOVE||pos.value!=0)ScaleTransition(scale:_scaleFactor,child:AnimatedBuilder(animation:_positionController,builder:(BuildContextcontext,Widget?child){//使用gif动画returnObx(()=>Container(height:getPos(pos.value),alignment:Alignment.bottomCenter,child:Container(padding:EdgeInsets.only(bottom:bottomImg),child:Image.asset("assets/gif_load.gif",width:imgHeight*2,height:imgHeight,),),));},),),Expanded(child:child),],)));}}enumMoveType{DOWN,MOVE,UP,}

代码如上,其中还额外使用了GetX来控制手势位移距离,然后再将末尾的assets/gif_load.gif更换为各自需要的gif资源即可。

 </div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
本文:怎么使用Flutter刷新组件RefreshIndicator自定义样式demo的详细内容,希望对您有所帮助,信息来源于网络。
上一篇:docker怎么查看容器启动命令下一篇:

10 人围观 / 0 条评论 ↓快速评论↓

(必须)

(必须,保密)

阿狸1 阿狸2 阿狸3 阿狸4 阿狸5 阿狸6 阿狸7 阿狸8 阿狸9 阿狸10 阿狸11 阿狸12 阿狸13 阿狸14 阿狸15 阿狸16 阿狸17 阿狸18