找回密码
 会员注册
查看: 18|回复: 0

Native和Flutter混合开发ViewPager的解决方案

[复制链接]

2

主题

0

回帖

7

积分

新手上路

积分
7
发表于 2024-10-9 16:55:38 | 显示全部楼层 |阅读模式
Native和Flutter混合开发ViewPager的解决方案 Native和Flutter混合开发ViewPager的解决方案 于光学 贝壳产品技术 贝壳产品技术 “贝壳产品技术公众号”作为贝壳官方产品技术号,致力打造贝壳产品、技术干货分享平台,面向互联网/O2O开发/产品从业者,每周推送优质产品技术文章、技术沙龙活动及招聘信息等。欢迎大家关注我们。 242篇内容 2021年12月17日 18:14 1背景Flutter由于其可以比拟原生的页面渲染速度,越来越多的开发者和企业愿意使用Flutter来开发或者混合开发模式开发APP,来提升开发效率,降低开发成本。贝壳在IPD和Android平板上,也进行了大量的Flutter实践。其中有一个场景如下:这个场景中,可以通过上下滑动来进行页面切换,而且其中一个是地图页面,这个页面是Native页面,而其它页面我们打算用Flutter来开发,想要实现这种效果就需要Native和Flutter在同一级页面的混合。最后的效果就是上下滑动或者点击左侧TAB可以任意切换Native和Flutter页面。2解决方案这种场景我们最先想到的就是用ViewPager+Fragment来开发,这是合理的解决方案,更好的消息是ViewPager2已经支持了上下滑动进行页面切换。挺简单的不是吗?我们开始吧。第一步:创建FlutterFragment第二步:创建NativeFragment 省略...第三步:组装ViewPager 省略...非常简单吧,一定不要高兴的太早,高兴太早的结果就是各种问题就会从四面八方不断涌出。问题一Native页面(自身可滚动的Native页面)与ViewPager的上下滑动切换冲突这个是原生的滚动冲突问题,老生常谈,网上解决方案数不胜数。咱们简单数一下,一般我们解决这种事件冲突大多数的方法就是继承ViewPager重写它的OnInterceptTouchEvent方法重新定义拦截规则或者重写它的dispatchTouchEvent方法重新定义事件的分发规则,但是ViewPger2有一个问题,如下:ViewPager2是 final 的类,他不允许被继承,那么这给我们解决这个问题,提供了一些的难度,但是问题不大,我们可以通过重写Fragment中外层的滚动控件,来实现同样的效果,原理类似,简单带过一下问题二Flutter页面(自身可滚动时)与ViewPager的滚动冲突同上面的原理一致,Native的ViewPager拦截了滑动事件,Flutter页面就没法滚动了,我们没法向上面一样去从Flutter页面中控制ViewPager的事件拦截。那么,Native到Flutter的滑动事件是如何传递的呢?Native到Flutter的事件传递过程示意图:这个图显示了Native到Flutter的事件传递过程,其实中间的传递过程我们并不关心,我们只关心传递链末端的事件结果,或者说,我们可以从其它角度来探索这个问题,我们只要知道Flutter页面滚动到底部或者顶部就可以了。方案示意图:2-1、ViewPager如何释放事件2-2、如何确定Flutter页面的滚动位置?想要确定Flutter页面的滚动位置,我们就要了解Dart语言的滚动机制,但是flutter的滚动机制不是我们要重点讨论的对象,而且要深入了解滚动原理我们需要用一篇甚至几篇的文章来解析,这里我们大概说一下flutter的滚动处理流程:当手指在屏幕上滑动额时候,首先通过RawGestureDetector收集手势信息(里面处理了手势竞争),竞争结束后将手势信息到Scrollable中。Scrollable接受到信息后,通过ScrollPosition进行滑动控制包含:(1)修改偏移量,通知Viewport绘制不同的区域(2)通知ScrollController,进行观察者的通知。判断滚动位置的方式:第一种方式--ScrollController我们可以自定义ScrollController,在这个controller中添加一个listener,通过上面的监听来判断滚动位置。但是对于我们这个场景来说ScrollController并不能完全满足我们的需求。ScrollController的主要作用:-----控制滚动控件的滚动位置-----获取当前的滚动位置对于解决滚动冲突来说这是不够的,从Android的解滚动冲突的经验来说,我们需要让父控件拦截或者透传我们的滚动事件,如果是这样的话我们还需要能够拦截滚动事件的传递,而ScrollController不能为我们提供这类的帮助。2、第二种方式--NotificationListenerAndroid的事件传递机制我们是清楚的(从上到下的(不拦截)传递,从下到上的拦截(不传递)),Flutter中有没有类似的事件传递机制呢?很高兴,有!Notification。通知(Notification) 是 Flutter 中重要的机制,在 Widget 树中的任一节点都以分发通知,通知会沿着当前节点向上传递冒泡,其父节点都可以用 NotificationListener 来监听或拦截通知。Flutter中很多地方使用了通知,如可滚动组件(Scrollable Widget)滑动时就会分发滚动通知(ScrollNotification),而Scrollbar正是通过监听ScrollNotification来确定滚动条位置的。而NotificationListener正式用来监听Notification的ScrollView的注释中也明确说明,我们可以用NotificationListener来监听滚动位置而不用使用ScrollController对于滚动事件来说Notification有下面几种:ScrollStartNotification:开始滚动ScrollUpdateNotification:正在滚动ScrollEndNotification : 结束滚动OverscrollNotification:滚动到边界OverscrooNotification?这不就是我们需要的吗~代码如下:这样我们就很简单的能够知道是不是滚动到边界了,我们还不知道是滚动到顶部还是底部,怎么办?继续通过ScrollStartNotification记录了初始位置后,通过滚动位置的差值来判断滚动方向和边界位置,然后向native发送通知(这里向Native发送通知也就是Flutter和Native的通信就不具体说了,我这里Andorid用的是EventBus,IOS用的是notification),Native收到通知后,ViewPager回收触摸事件自己处理,这样ViewPager就可以翻页了!问题三不可滚动的Flutter页面的滚动冲突我们用上面的自定义的NotificationListener包裹了我们的Flutter页面,发现可滚动的Flutter页面已经没问题了,但是不可滚动的Flutter页面无法通过滚动切换ViewPager。原因是,这类页面不会发出ScrollNotification,我们需要添加手势监听来解决。添加手势监听解决滚动冲突:这样我们这个自定义的NotificationListener基本完成,我们可以用它来包装我们的widget,放到Native的ViewPager中使用了。Native的处理当然,Native侧我们还需要做一定的处理,来呼应Flutter给到的消息通知,Native侧,接收到滚动到边界的通知后,还需要让ViewPager重新拿回事件处理权,处理页面切换。效果图:问题四使用同一引擎的Flutter页面切换闪屏问题但是这时又遇到了一个问题:如果是相邻的Flutter页面进行切换会出现 闪屏的情况。闪屏现场如下:闪屏的原因:Tab2和Tab3的flutter页面使用的是同一个FlutterEngine去绑定。在Flutter1.12上,当它们之间发生切换的时候,会先执行FlutterView的detachFromFlutterEngine()方法将当前FlutterFragment的FlutterView和FlutterEngine解绑;然后去和另外一个FlutterFragment的FlutterView进行重新绑定,重新绑定是需要一定的时间的,所以会在下一个Flutter页面真正显示出来之前先显示之前绑定的页面,然后下一个FlutterView真正绑定建立连接之后会重新刷新视图发生变化,这就造成了Tab切换视图的闪屏问题。当然我们可以给每一个Flutter页面单独开一个引擎来解决这个问题,但是这种方式对内存和性能的消耗是挺大的,我这里用了一种巧妙的方式来解决了这个问题:把所有相邻的Flutter页面用Flutter的PageView包裹起来,这样相邻的Flutter页面就相当于一个页面一个引擎,Flutter页面的切换就仅仅是Flutter中PageView的切换而已,不需要引擎的解绑和绑定了。修改前:修改前的方案是正常的一个tab对应一个Fragment,而创建Flutter Fragment时都用的同一个引擎,所以在页面切换时,就会进行引擎的解绑和绑定而出现上面的闪屏问题。修改后:如上图所示,4个tab对应了2个Fragment,其中一个Fragment对应的是Flutter的PageView,这样通过Native和Flutter之前的通信来实现每个4个tab对应的4个页面。这样在切换Flutter页面时,就仅仅是Flutter 的PageView内部切换而不存在引擎的解绑和绑定问题了。3总结贝壳在PAD上实践Flutter的过程当中,踩过很多的坑,也总结很多的经验;本篇文章是我们在混合开发过程当中使用混合ViewPager的过程,遇到的问题以及解决方案,这里只说了Android端的实现过程,IOS思路一致,目前该方案用在必行PAD项目中作为项目基础框架体验还不错,在此分享给大家,砥砺同行,共同进步! 预览时标签不可点 移动端37Flutter15大前端69移动端 · 目录#移动端上一篇贝壳鸿蒙开发实践下一篇209M->102M,贝壳B端iOS包瘦身之路关闭更多小程序广告搜索「undefined」网络结果
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2025-1-4 05:37 , Processed in 0.450270 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表