前言

在2021年末,酷狗发布了最新版11.0.0版本,这是一次重大的ui重构,更新完打开着实让我耳目一新。在原有风格上,整个app变得更加清爽,流畅。其中tabbar的风格让我非常感兴趣,如果用flutter来实现,或许是一个很有趣的事情。

效果图

分析效果

研究酷狗tabbar的动画可以发现,默认状态下在当前tab的中心处展示圆点,滑动时的效果拆分成两个以下部分:

  • 从单个tab a的中心根据x轴平移到tab b的中心位置;
  • 指示器的长度从圆点变长,再缩短为圆点。其中最大长度是可变的,跟两个tab的大小和距离都有关系;
  • 指示器虽然依赖tab的size和offset来变换,但和tab却基本是同一时间渲染的,整个过程非常顺滑;
  • 总的来说,酷狗的效果就是改变了指示器的渲染动画而已。

开发思路

从上面的分析可以明确,指示器的滑动效果一定跟每个tab的size和offset相关。那在flutter中,获取渲染信息我们马上能想到globalkey,通过globalkey的currentcontext对象获取rander信息,但这必须在视图渲染完成后才能获取,也就是说tab渲染完才能开始计算并渲染指示器。很显然不符合体验要求,同时频繁使用globalkey也会导致性能较差。

转变思路,我们需要在tab渲染的不断把信息传给指示器,然后更新指示器,这种方式自然想到了custompainter【之前写了很多canvas的控件,都是根据传入的值进行绘制,从而实现控件的变化了layout类】。在tab updatewidget的时候,不断把rander的信息传给画笔painter,然后更新绘制,理论上这样做是完全行得通的。

flutter tabbar 解析源码

为了验证我的思路,我开始研究官方tabbar是如何写的:

  • 进入tabbar类,直接查看build方法,可以看到为每个tab加入了globalkey,然后指示器用custompaint进行绘制;
  • 绘制指示器用custompaint跟我们的预想一致,那如何把绘制的size和offset传进去呢。我们来看_tablabelbar继承于flex,而flex又继承自multichildrenderobjectwidget,重写其createrenderobject方法;

查看真实的渲染对象:_tablabelbarrenderer,在performlayout中返回渲染的size和offset,并通过tabbar传入的_savetaboffsets方法保存到_indicatorpainter中;_savetaboffsets尤为重要,把tabbar的渲染位移通知给painter,从而让painter可以轻松算出tab之间的宽度差

  • 通过tabbar中的didchangedependencies和didupdatewidget生命周期,更新指示器;
  • 然后重点就在指示器_indicatorpainter如何进行绘制了。

实现步骤

通过理解flutter tabbar的实现思路,大体跟我们预想的差不多。不过官方继承了flex来计算offset和size,实现起来很优雅。所以我也不班门弄斧了,直接改动官方的tabbar就可以了。

  • 创建kugoutabbar,复制官方代码,修改引用,删除无关的类,只保留tabbar相关的代码。

2. 重点修改_indicatorpainter,根据我们的需求来绘制指示器。在painter方法中,我们可以通过controller拿到当前tab的index以及animation!.value, 我们模拟下切换的过程,当tab从第0个移到第1个,动画的值从0变成1,然后动画走到0.5时,tab的index会从0突然变为1,指示器应该是先变长,然后在动画走到0.5时,再变短。因此动画0.5之前,我们用动画的value-index作为指示器缩放的倍数,指示器不断增大;动画0.5之后,用index-value作为缩放倍数,不断缩小。

而指示器接收缩放倍数的前提还需要计算指示器最大的宽度,并且上面是根据动画的0.5作为最大的宽度,也就是移动到一半的时候,指示器应该达到最大宽度。因此指示器最大的宽度是需要️2的。请看下面代码:

  • 如上,指示器的宽度我们根据controller切换时的index和动画值进行转化,实现宽度的变化。而offset的最小值和最大值分别是切换前后两个tab的中心点,这里应该做下相应的的限制,然后传给rect.fromltwh。

【由于时间和精力问题,我并没有去做这一步的实现,而且酷狗那边动画跟滑动逻辑的关系需要ui给出具体的公式,才能百分百还原。】

最后就是加多一个参数,让业务方传入指示器的最小宽度。

业务使用

在上面我们已经把简单的动画效果改完了,接下来就是传入圆角的indicator、最小宽度indicatorminwidth,就可以正常使用啦。

  • 圆角的指示器,我直接上源码
  • 调用非常简单,跟原来官方代码一模一样。

写在最后

模仿酷狗的tabbar效果,就分享到这里啦,重点在于实现步骤的第2、3步,涉及到一些简单的数学知识。说说心得吧,flutter ui层面的问题,其实技术栈已经很单一了。只要跟着官方的实现思路,能写出跟其类似的代码,把rander层理解透彻,笔者认为已经足够了。往深了还是得往原生、混编、解决flutter痛点问题为主。 希望一起共勉!!!

实现源码

https://github.com/wxqkb/kugoutabbar.git

到此这篇关于如何利用flutter实现酷狗流畅tabbar效果的文章就介绍到这了,更多相关flutter实现酷狗tabbar效果内容请搜索www.887551.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持www.887551.com!