flutter list排序_最近 Flutter 争气了! Flutter 也可以做这么炫酷的动画

在 2019 年的谷歌 I/O 大会上,开发团队发布了 Flutter for web 的首个技术预览版,宣布 Flutter 正在为包括 Google Home Hub 在内的 Google 智能显示平台提供支持,并通过结合 Chrome OS 为桌面级应用程序提供支持迈出第一步。

一张图感受下

8d376175888621ddb6e7be0d6c599218.png

本篇给大家展示一个如何用Flutter做炫酷动画!可能很多同学对 Flutter 还不了解,没关系,可以通过全文类比于 Android 上制作动画的区别与相似之处!

前言

这一段时间,Flutter的势头是越来越猛了,作为一个Android程序猿,我自然也是想要赶紧尝试一把。在学习到动画的这部分后,为了加深对Flutter动画实现的理解,我决定把之前写的一个卡片切换效果的开源小项目,用Flutter“翻译”一遍。

废话不多说,先来看看效果吧:

Android:

c92bb5c72b629e2a251a25153fbb36c3.gif

IOS

a92f3d80e8af272b2cb7624d04469e5a.gif

Github地址:

https://github.com/BakerJQ/Flutter-InfiniteCards

思路

  • 首先,关于卡片的层叠效果,在原Android项目中,是通过Scale差异以及TranslationY来体现的,Flutter可以继续采用这种方式。
  • 其次,对于自定义卡片的内容,原Android项目是通过Adapter实现,对于Flutter,则可以采用IndexedWidgetBuilder实现。
  • 最后,就是自定义动效的实现,原Android项目是通过一个0到1的ValueAnimator来定义动画的展示过程,而Flutter中,正好有与之对应的Animation和AnimationController,如此我们就可以直接自定义一个动画过程中,具体的视图展示方式。

组件总览

由于卡片视图需要根据动画情况进行渲染,所以显然是一个StatefulWidget。

同时,我们给出三种基本的动画模式:

enum AnimType { TO_FRONT,//被选中的卡片通过自定义动效移至第一,其他的卡片通过通用动效补位 SWITCH,//选中的卡片和第一张卡片互换位置,并都是自定义动效 TO_END,//第一张图片通过自定义动效移至最后,其他卡片通过通用动效补位}

并通过Helper和Controller来处理所有的动画逻辑

其中Controller由构造方法传入

InfiniteCards({ @required this.controller, this.width, this.height, this.background,});

Helper在initState中进行构建,并初始化,同时将Helper绑定给Controller:

@overridevoid initState() { ... _helper = AnimHelper( controller: widget.controller, //传入动画更新监听,动画时调用setState进行实时渲染 listenerForSetState: () { setState(() {}); }); _helper.init(this, context); if (widget.controller != null) { widget.controller.animHelper = _helper; }}

而build过程中,则通过Helper返回具体的Widget列表,而Stack则是为了实现层叠效果。

 ... return Container( ... child: Stack( children: _helper.getCardList(_width, _height), ), );}

如此,基本的初始化等操作就算是完成了。下面我们来看看Controller和Helper都是怎么工作的。

Controller

我们先来看看Controller所包含的内容:

class InfiniteCardsController { //卡片构造器 IndexedWidgetBuilder _itemBuilder; //卡片个数 int _itemCount; //动画时长 Duration _animDuration; //点击卡片是否触发切换动画 bool _clickItemToSwitch; //动画Transform AnimTransform _transformToFront,_transformToBack,...; //排序Transform ZIndexTransform _zIndexTransformCommon,...; //动画类型 AnimType _animType; //曲线定义(类Android插值器) Curve _curve; //helper AnimHelper _animHelper; ... void anim(int index) { _animHelper.anim(index); } void reset(...) { ... //重设各参数 setControllerParams(); _animHelper.reset();  ... }}

由此可以看到,Controller基本上就是作为参数配置器和Helper的方法代理的存在。由此童鞋们肯定就知道了,对于动效的自定义和动效的触发等操作,都是通过Controller来完成,demo如下:

//构建Controller_controller = InfiniteCardsController( itemBuilder: _renderItem, itemCount: 5, animType: AnimType.SWITCH,);//调用reset_controller.reset( itemCount: 4, animType: AnimType.TO_FRONT, transformToBack: _customToBackTransform,);//调用展示下一张卡片动画_controller.reset(animType: AnimType.TO_END);_controller.next();

关于具体的自定义,我们稍后再聊,咱们先来看看Helper。

Helper

Helper是整个动画效果实现的核心类,我们先看几个它所包含的核心成员:

class AnimHelper { final InfiniteCardsController controller; //切换动画 AnimationController _animationController; Animation _animation; //卡片列表 List _cardList = new List(); //需要向后切换的卡片,和需要向前切换的卡片 CardItem _cardToBack, _cardToFront; //需要向后切换的卡片位置,和需要向前切换的卡片位置 int _positionToBack, _positionToFront;}

现在我们来看看,如果要触发一个切换动画,这些成员是如何相互配合的。

当选中一张卡片进行切换时,这张卡片就是需要向前切换的卡片(ToFront),而第一张卡片,就是需要向后切换的卡片(ToBack)。

void _cardAnim(int index, CardItem card) { //记录要切换的卡片 _cardToFront = card; _cardToBack = _cardList[0]; _positionToBack = 0; _positionToFront = index; //触发动画 _animationController.forward(from: 0.0);}

由于设置了AnimationListener,在动画过程中,setState就会被调用,如此就会触发Widget的build,从而触发Helper的getCardList方法。

我们来看看在切换动画的过程中,是如何返回卡片Widget列表的。

List getCardList(double width, double height) { for (int i = 0; i < controller.itemCount; i++) { ... if (_isSwitchAnim) { //处理切换动画 _switchTransform(width, height, i); } ... } //根据zIndex进行排序渲染 List copy = List.from(_cardList); copy.sort((card1, card2) { return card1.zIndex < card2.zIndex ? 1 : -1; }); return copy.map((card) { return card.transformWidget; }).toList();}

如上代码所示,先进行动画处理,后根据zIndex进行排序,因为要保证在前面的后渲染。

而动画是如何处理的呢,以切换到前面的卡片为例:

void _toFrontTransform(double width, double height, int fromPosition, int toPosition) { CardItem cardItem = _cardList[fromPosition]; controller.zIndexTransformToFront( cardItem, _animation.value, _getCurveValue(_animation.value), width, height, fromPosition, toPosition); cardItem.transformWidget = controller.transformToFront( cardItem.widget, _animation.value, _getCurveValue(_animation.value), width, height, fromPosition, toPosition); }

原来,正是在这一步,Helper通过Controller中配置的自定义动画方法,得到了卡片的Widget。

由此,动画展示的基本流程就描述完了,下面我们进入最关键的部分–如何自定义动画。

自定义动画

我们以通用动画为例,来看看自定义动画的主要流程。

首先,AnimTransform为如下方法的定义:

typedef AnimTransform = Transform Function( Widget item,//卡片原始Widget double fraction,//动画执行的系数 double curveFraction,//曲线转换后的系数 double cardHeight,//整体高度 double cardWidth,//整体宽度 int fromPosition,//卡片开始位置 int toPosition);//卡片要移动到的位置``该方法返回的是一个Transform,专门用于处理视图变换的Widget,而我们要做的,就是根据传入的参数,构建相应系数下的Widget。以DefaultCommonTransform为例:```Transform _defaultCommonTransform(Widget item,  double fraction, double curveFraction, double cardHeight, double cardWidth, int fromPosition, int toPosition)  //需要跨越的卡片数量{ int positionCount = fromPosition - toPosition; //以0.8做为第一张的缩放尺寸,每向后一张缩小0.1 //(0.8 - 0.1 * fromPosition) = 当前位置的缩放尺寸 //(0.1 * fraction * positionCount) = 移动过程中需要改变的缩放尺寸  double scale = (0.8 - 0.1 * fromPosition) + (0.1 * fraction * positionCount); //在Y方向的偏移量,每向后一张,向上偏移卡片宽度的0.02 //-cardHeight * (0.8 - scale) * 0.5 对卡片做整体居中处理 double translationY = -cardHeight * (0.8 - scale) * 0.5 - cardHeight * (0.02 * fromPosition - 0.02 * fraction * positionCount); //返回缩放后,进行Y方向偏移的Widget return Transform.translate( offset: Offset(0, translationY), child: Transform.scale( scale: scale, child: item, ), );}

对于向第一位移动的选中卡片,也是同理,只不过是根据该卡片对应的转换器来进行自定义动画的转换。

最后的效果,就像演示图中第一次点击,图片向前翻转到第一位的效果一样。

总结

由于Flutter采用的是声明式的视图构建方式,在编码初期,多少会受到原生编码方式的思维影响,而觉得很难受。但是在熟悉了之后,就会发现其实很多思想都是共通的,比如Animation,比如插值器的概念等等。

另外,研读源码仍然是最有效的解决问题的方式,比如相比Android中直接对ScrollView进行animateTo操作,在Flutter中需要通过ScrollController进行animateTo操作,正是这一点让我找到了在Flutter中实现InfiniteCards效果的方法。

更具体的Demo请前往Github的Flutter-InfiniteCards Repo,欢迎大家star和提issue。

再次贴一下Github地址:

https://github.com/BakerJQ/Flutter-InfiniteCards

### 文末送福利啦!

为此我整理了一些以往自己学习的视频资料,如果有需要借鉴学习的开发者可以联系我免费获取共同进步,包括上文讲到的flutter (Flutter丶Glide丶OPencv丶EventBus丶自定义View丶数据库框架设计丶插件化组件化丶Binder等都有对应的视频教学以及一些面试题)免费分享给大家。

824a5a4c1f79af321c0f840edc9c0018.png

资料免费领取方式:关注我后台私信关键词【资料】获取!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值