preface

The pub currently has two of the most liked plugins for WebView,

Webview_flutter and flutter_webview_pluginCopy the code

After some comparison, I chose the latter: flutter_webview_plugin. I will write the record here in the hope that it will help you

The difference between

Webview_flutter:

Flutter official development and maintenance, using platformView display. Controlled by the flutter end (within the tree), the page transition animation is coordinated and controlled.Copy the code

Flutter_webview_plugin:

Flutter is developed and maintained by the community using the native method of adding renderings. Because it is drawn native, not in the flutter tree, and not under its control, show and hide are notified by methodChannel.Copy the code

The former may seem more flexible than the latter, but the single most serious penalty is performance:

The performance of webview_flutter is significantly weaker than that of Flutter_webview_plugin. The lag caused by webview_flutter is visible to the naked eye, and there is no need to look at FPS, Dumpsy… Especially for slightly more complex pages.

For this reason I chose flutter_webview_plugin, although it has its drawbacks.

flutter_webview_plugin

Problems encountered

Because it is rendered native (in Android’s case, by addContentView(WebView)), it is not in the widget tree of a flutter and has no control over it.

So the page when we adopt the transition animations, such as sliding entry/exit, due to flutter the page without the transition animation, is not really exit (dispose), reveals the hidden and released from the plug-in was conducted in the dispose of the page, this leads to, background while sliding out (or leak out of the upper page), But the content of the WebView remains for a while.

Problems demonstrate

Problem analysis

I have checked the source code of Flutter_webview_plugin. Its UI structure and running process are shown in the following figure

Code structure

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

After the paint method of the created renderBox is called, the onRectChanged method is called back with the display area RECT and passed

Webviewreference. launch launches the view on the native side to add a rendering area based on the passed RECT.

WebviewReference extends FlutterWebviewPlugin This class is a communication class that also exposes a resize method to adjust when recT changesCopy the code
  /// 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);
  }
Copy the code

After the above analysis, we can change the display position and size of the WebView by changing the recT.

The first thing that comes to mind is the PageRouteBuilder that animates pages;

First version solution

After the PageRouteBuilder such source code layer by layer analysis

PageRouteBuilder has a lot of nesting, and I've also taken a look at the push method. I've put a rough flow chart at the end of this article for those interestedCopy the code

It is found that the transition animation can be listened to by builder.animation

SlideRightRouteBuilder builder = SlideRightRouteBuilder(ComplexPage()); Navigator.of(context).push(builder); / / / to be placed behind the push, otherwise an error, because see article at the end of the flow chart builder. The animation. The addListener (() {});Copy the code

So I pass a key to ComplexPage, use this to get the context, get its offset, and then do the following in the callback function

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

The offset is the parent widget that wraps the WebView. It is in the widget tree and is controlled by the animation, in other words, the offset changes as the animation progresses.

And then we just need to call

webviewReference.resize(_rect.shift(offset));
Copy the code

During this process, because the Builder and resize were in different widgets (pages) and could only be transferred/called through various interfaces, serious coupling occurred, and after considering the need to be compatible with slide/zoom animations and pr into the plugin repository, this approach was abandoned.

Final solution – Compatible with sliding/scaling

After re-thinking, it was found that the dependence on Builder was just listening to animation and triggering resize. For the progress value, it could be solved by other methods. So we have the following scheme.

First I defined the supported transition to animation type in the plugin’s communication class FlutterWebviewPlugin

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

The corresponding parameters were then added to the constructor of the WebviewScaffold plug-in

final TransitionType transitionType;
Copy the code

Call the method I created in initState () of state:

perceptionPageTransition();
Copy the code
  /// 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();
         }
       });
     });
   }
 }
Copy the code

Using this method, I can simulate the listener of Builder. animation. Now look at the driveWebView() method

void driveWebView(){ final RenderBox box = context.findRenderObject(); final Offset offset = box.localToGlobal(Offset.zero); Final Size Size = box. Size; Final Rect Rect = box.paintbounds; If (offset. Dx == rect.left)return; // If (offset. Dx == rect.left)return; Value final double value = offset. Dx /size. Width; 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; }}Copy the code

This completes the functionality of the first version while decoupling the plug-in from the project.

rendering

In debug mode, the first transition will have a mismatch and then the normal performance mode and release will be completely normalCopy the code

Some flow charts recorded during analysis

.push()

pageRouteBuilder

conclusion

Hope the above is helpful to you, if not welcome to point out, like a zansa;)

The project has been PR, the boss is helping to review, I don’t know when to merge.

Fork’s warehouse address

My other articles

Flutter hybrid development – an alternative but efficient native View embedding method

A look at Flutter’s performance in game development and its cross-platform advantages

Flutter – encapsulates a Loading popup for easy use

Flutter custom View – a list of self-selected stocks that mimic a flush