【Flutter】Bug Or 特性?一个属性引起的绘制流程的问题

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。

前言

在前面的文章中,提到了我会将翻页动画集成进LayoutManager中,在此过程中遇到了一个有点意思的问题;

如果在最普通的ListView中,仅仅提供itemBuilder等提供item所需的必要部分;其他属性并不作修改;

修改其中的paint方法,如果将saveLayer的BlendMode改为dst,下面这段代码的运行结果是?

image.png

结果正如预想中的那样,由于dst属性生效,所以展示的其他页面,比如说第一页:

image.png

下面有点意思的东西来了,如果我解开上面的paintChild部分的注释,讲道理,dst应该接着生效吧,其实不然,解开注释之后的效果,竟然是drawColor的clear效果,一片背景的黑色:

image.png

不过这个问题只需要修改ListView的一个属性,将其改为false即可:

image.png

那么问题来了,这个addRepaintBoundaries都干了什么?为什么saveLayer这种新建图层的操作的效果都给强制取消掉了?

分析

在前面的文章中已经提到过,addRepaintBoundaries 的唯一作用就是给Item包一层 RepaintBoundary;那问题就出自这块了;

而出现这个问题的另一个必要因素就是context.paintChild 方法,那么看一下这个方法:

image.png

在paintChild方法中,抛开assert 部分,剩下的就一处 if else 判断,而且正好是跟 RepaintBoundary 有关的 isRepaintBoundary 判断;

child._paintWithContext 方法自不用多说,其中就是调用child自己本身的paint方法,属于挺正常的逻辑;而这个stopRecordingIfNeeded 和 _compositeChild 就应该是问题所在;

第一个stopRecordingIfNeeded,根据注释,无非是停止绘制内容,而非改变绘制,所以可以排除;那么第二个_compositeChild 方法就是问题所在了;

而其内容也不多:

image.png

总结一下:如果child需要重绘,那么调用 repaintCompositedChild,否则就调用 appendLayer;

而这个 repaintCompositedChild 应该是调不到的,child一直不需要重绘,那就需要再看下这个 appendLayer 方法:

image.png

这里出现了Flutter的第四棵树的部分———— Layer 树,也就是最后一步,最终传给引擎层的树;(一般我们只关注前三棵树……没想到也有来看第四棵树的一天……)

而这个方法所做的事非常简单,就是将原来的Layer树结构改变,解绑原Layer,并加入到心的ContaienrLayer中;

而这个ContaienrLayer出自也很简单,就一处:

image.png

那么回到最开始的paint方法,串联一下,如果由RepaintBoundary,所做的事就是将RepaintBoundary的layer树加到父widget对应的layer中,就这么简单~

但是问题来了,如果我没错的话,这里有个问题:

canvas呢,我为canvas创建的layer呢?是这样被新add上去的layer替换掉了么?

image.png

个人感觉问题就在这里,由于saveLayer这种都是native方法,没有直接去看,所以以上猜测纯属脑补;

但是考虑到 saveLayer 这种native方法应该不至于跨语言跟dart层共享数据,我这猜测正确的概率,大概55开???

总之可以肯定的是,去掉RepaintBoundary,就能正常处理;至于这块是人为疏漏,忘记了saveLayer的部分,还是就这么设计的,那就是另一回事了…………不过由此导致的困扰确实是真实存在的…………

结尾

这个问题花了2小时,甚至摸鱼时间都用来解决这个问题……因此今天也就将原来逻辑移到LayoutManager中;就不放新进度效果图了,进度约等于0;

不过往好处想想,找出了解决方案,不至于半夜床中惊坐起,自问此题如何解;

猜你喜欢

转载自juejin.im/post/7036339451299102756