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