修復(fù)flutter_webview_plugin在頁面滑出時web圖層殘留的問題

前言

目前pub上關(guān)于webview有兩個點贊最多的插件策治,

webview_flutter 和 flutter_webview_plugin

經(jīng)過一番比較選擇了后者:flutter_webview_plugin次绘,這里將記錄寫出來,希望對你有所幫助

兩者區(qū)別

webview_flutter :

flutter官方開發(fā)維護(hù),采用的platformView顯示。

受flutter端控制(在樹內(nèi))娩贷,對于頁面過渡動畫是可協(xié)調(diào),受控制的锁孟。

flutter_webview_plugin :

flutter 社區(qū)開發(fā)維護(hù)彬祖,采用的是原生端添加渲染的方式。

因為是原生端繪制罗岖,不在flutter 樹內(nèi)涧至,不受其控制,顯示和隱藏是需要methodChannel進(jìn)行通知的桑包。

看起來前者要比后者靈活方便,但是唯一也是最嚴(yán)重的扣分項就是性能問題 :

webview_flutter 的性能要明顯弱于 flutter_webview_plugin纺非,其所造成的卡頓是肉眼可見哑了,不需要看什么fps、dumpsy啥的...尤其是稍微復(fù)雜一些的頁面烧颖。

基于此我選擇了flutter_webview_plugin弱左,當(dāng)然它也有不足。

flutter_webview_plugin

遇到的問題

由于其本身是采用原生端渲染(以安卓為例炕淮,是通過addContentView(webview)),因此其不在flutter 的widget樹內(nèi)拆火,也就無從談起flutter對其的控制了。

那么當(dāng)我們的頁面采用了過渡動畫,如滑動進(jìn)入/退出们镜,由于flutter 頁面在沒有走完過渡動畫時币叹,是不會真正退出的(走dispose),而插件的顯隱和釋放是在頁面的dispose中才進(jìn)行的模狭,這就導(dǎo)致了颈抚,背景雖然滑出去了(或者漏出了上層頁面),但是webview的內(nèi)容依然殘留了一會才消失嚼鹉。

問題演示

image

問題分析

查看了flutter_webview_plugin的源碼贩汉,它的ui結(jié)構(gòu)和運行流程如下圖

image

代碼大致結(jié)構(gòu)

    class _WebviewScaffoldState extends state{
        widget build(){
            return Scaffold(
                body:_WebviewPlaceholder(
                    onRectChanged:(Rect rect){
                        webviewReference.launch(
                            rect:rect
                            ...
                        );
                    }
                )
            );
        }
    }

在創(chuàng)建的renderBox的paint方法調(diào)用后,就會回調(diào)onRectChanged 這個方法并攜帶顯示區(qū)域rect锚赤,然后通過

webviewReference.launch 啟動原生端的view添加繪制匹舞,繪制區(qū)域基于所傳的rect。

webviewReference extends FlutterWebviewPlugin 這個類是一個通信類线脚,

這個類還對外暴露了一個resize方法用于在rect改變時進(jìn)行相應(yīng)的調(diào)整
  /// resize webview
  Future<Null> resize(Rect rect) async {
    final args = {};
    args['rect'] = {
      'left': rect.left,
      'top': rect.top,
      'width': rect.width,
      'height': rect.height,
    };
    await _channel.invokeMethod('resize', args);
  }

經(jīng)過上面的分析策菜,只要我們改動這個rect就可以改變webview的顯示位置和大小。

首先我想到的是對頁面做動畫的PageRouteBuilder;

初版解決方案

經(jīng)過對PageRouteBuilder這類的源碼一層一層分析后

PageRouteBuilder 嵌套極多酒贬,同時我還捎帶了看了一下push方法又憨,所得的大致的流程圖我放在文章結(jié)尾,有興趣的可以看一下

發(fā)現(xiàn)通過builder.animation可以對過渡動畫進(jìn)行監(jiān)聽

              SlideRightRouteBuilder builder = SlideRightRouteBuilder(ComplexPage());
              Navigator.of(context).push(builder);
              ///要放在push后面锭吨,不然報錯蠢莺,原因見文章末尾的流程圖
              builder.animation.addListener(() {

              });

那么我給ComplexPage傳入一個 key,通過這個獲取context零如,進(jìn)而取到它的offset,然后在回調(diào)函數(shù)中執(zhí)行以下操作

final RenderBox box = context.findRenderObject();
final Offset offset = box.localToGlobal(Offset.zero);

這個offset就是包裹webview的那個父widget躏将,它是在widget樹上,受動畫控制的考蕾,換言之隨著動畫的進(jìn)行祸憋,這個offset也會變化。

之后我們只需要調(diào)用

webviewReference.resize(_rect.shift(offset));

在這個過程中肖卧,因為builder和resize分別在不同的widget(頁面)蚯窥,只能通過各種接口傳輸/調(diào)用,這樣就發(fā)生了嚴(yán)重的耦合塞帐,在考慮需要兼容 滑動/縮放動畫拦赠,并pr到插件倉庫后,便直接放棄了這個方法葵姥。

終版解決方案-兼容滑動/縮放

重新思考荷鼠,發(fā)現(xiàn)對于builder的依賴,只是對animation的監(jiān)聽榔幸,并觸發(fā)重繪(resize)允乐,對于進(jìn)度值矮嫉,完全可以通過其他方法解決。所以便有了下面的方案牍疏。

首先我在插件的通信類FlutterWebviewPlugin,定義了支持的過渡到動畫類型

/// the transition animation type of page on/off screen
enum TransitionType{
  Non,
  Slide,
  Scale
}

之后在插件WebviewScaffold的構(gòu)造函數(shù)中增加了對應(yīng)的參數(shù)

final TransitionType transitionType;

在state的initState()中調(diào)用我創(chuàng)建的方法:

perceptionPageTransition();
  /// coordinate the webview rect whit page's transition
 void perceptionPageTransition(){
   if(widget.transitionType != TransitionType.Non){
     WidgetsBinding.instance.addPostFrameCallback((timeStamp) {
       //avoid to concurrent modification exception
       WidgetsBinding.instance.addPersistentFrameCallback((timeStamp) {
         if(context != null){
           driveWebView();
         }
       });
     });
   }
 }

通過上面這個方法蠢笋,我就可以模擬出builder.animation的監(jiān)聽了。再看driveWebView()方法


  void driveWebView(){
   final RenderBox box = context.findRenderObject();
   final Offset offset = box.localToGlobal(Offset.zero);
   //獲得可繪制的大小
   final Size size = box.size;
   //獲得可繪制的區(qū)域
   final Rect rect = box.paintBounds;
   
   //當(dāng)變動位置等于繪制區(qū)域的位置時麸澜,說明動畫已經(jīng)執(zhí)行完畢挺尿,直接退出,避免過度繪制
   if(offset.dx == rect.left)return;
   //這個值用于縮放動畫
   //根據(jù)當(dāng)前位置的dx值/除以size的寬度炊邦,就可以計算出動畫進(jìn)度value
   final double value = offset.dx/size.width;
   //根據(jù)傳入的動畫類型编矾,對rect進(jìn)行位移或者縮放
   switch(widget.transitionType){
     case TransitionType.Slide:
       webviewReference.resize(_rect.shift(offset));
       break;
     case TransitionType.Scale:
       final double www = box.size.width*(value*2);
       final double hhh = box.size.height*(value*2);
       webviewReference.resize(Rect.fromLTWH(offset.dx,offset.dy,size.width-www , size.height-hhh));
       break;
     case TransitionType.Non:
       // TODO: Handle this case.
       break;
   }
 }

這樣我們就完成了初版的功能,同時使插件和項目進(jìn)行了解耦馁害。

效果圖

debug模式下窄俏,第一次過度會有錯位之后正常
性能模式和release 則完全正常
image
image

分析時記錄的一些流程圖

.push()

[圖片上傳失敗...(image-8f4dbc-1598493593844)]

pageRouteBuilder

image

結(jié)語

希望以上對你有所幫助,如果不足之處歡迎指出碘菜,喜歡的點個贊撒 ;)

該項目已經(jīng)PR凹蜈,大佬正在幫忙審核,不知道什么時候合并忍啸。

Fork的倉庫地址

我的其它文章

Bedrock——基于MVVM+Provider的Flutter快速開發(fā)框架

Flutter自定義View——仿高德三級聯(lián)動Drawer

Flutter 自定義View——仿同花順自選股列表

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仰坦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子计雌,更是在濱河造成了極大的恐慌悄晃,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件凿滤,死亡現(xiàn)場離奇詭異妈橄,居然都是意外死亡,警方通過查閱死者的電腦和手機翁脆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門眷蚓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人反番,你說我怎么就攤上這事沙热。” “怎么了恬口?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵校读,是天一觀的道長。 經(jīng)常有香客問我祖能,道長,這世上最難降的妖魔是什么蛾洛? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任养铸,我火速辦了婚禮雁芙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钞螟。我一直安慰自己兔甘,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布鳞滨。 她就那樣靜靜地躺著洞焙,像睡著了一般。 火紅的嫁衣襯著肌膚如雪拯啦。 梳的紋絲不亂的頭發(fā)上澡匪,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天,我揣著相機與錄音褒链,去河邊找鬼唁情。 笑死,一個胖子當(dāng)著我的面吹牛甫匹,可吹牛的內(nèi)容都是我干的甸鸟。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼兵迅,長吁一口氣:“原來是場噩夢啊……” “哼抢韭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起恍箭,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤刻恭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后季惯,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吠各,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年勉抓,在試婚紗的時候發(fā)現(xiàn)自己被綠了贾漏。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡藕筋,死狀恐怖纵散,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情隐圾,我是刑警寧澤伍掀,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布,位于F島的核電站暇藏,受9級特大地震影響蜜笤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜盐碱,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一把兔、第九天 我趴在偏房一處隱蔽的房頂上張望沪伙。 院中可真熱鬧,春花似錦县好、人聲如沸围橡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翁授。三九已至,卻和暖如春晾咪,著一層夾襖步出監(jiān)牢的瞬間收擦,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工禀酱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留炬守,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓剂跟,卻偏偏與公主長得像减途,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子曹洽,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345