From a9aedf96d378dbf7423f1abd7e20f4cbfcbc66db Mon Sep 17 00:00:00 2001
From: "mowen@126.com" <mowen@126.com>
Date: Sun, 30 May 2021 02:56:14 +0800
Subject: [PATCH 1/5] Fixed a crash when load local HTML resources on IOS when
 localUrlScope attribute is null

Fixed a crash when load local HTML resources on IOS when localUrlScope attribute is null
---
 ios/Classes/FlutterWebviewPlugin.m | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/ios/Classes/FlutterWebviewPlugin.m b/ios/Classes/FlutterWebviewPlugin.m
index fc266094..bfd14991 100644
--- a/ios/Classes/FlutterWebviewPlugin.m
+++ b/ios/Classes/FlutterWebviewPlugin.m
@@ -196,16 +196,10 @@ - (void)navigate:(FlutterMethodCall*)call {
             NSString *url = call.arguments[@"url"];
             NSNumber *withLocalUrl = call.arguments[@"withLocalUrl"];
             if ( [withLocalUrl boolValue]) {
-                NSURL *htmlUrl = [NSURL fileURLWithPath:url isDirectory:false];
-                NSString *localUrlScope = call.arguments[@"localUrlScope"];
                 if (@available(iOS 9.0, *)) {
-                    if(localUrlScope == nil) {
-                        [self.webview loadFileURL:htmlUrl allowingReadAccessToURL:htmlUrl];
-                    }
-                    else {
-                        NSURL *scopeUrl = [NSURL fileURLWithPath:localUrlScope];
-                        [self.webview loadFileURL:htmlUrl allowingReadAccessToURL:scopeUrl];
-                    }
+                    NSURL *scopeUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
+                    NSURL *fileUrl = [NSURL fileURLWithPaht:[[NSBundle mainBundle] pathForResource:url ofType:nil]];
+                    [self.webview loadFileURL:fileUrl allowingReadAccessToURL:scopeUrl];
                 } else {
                     @throw @"not available on version earlier than ios 9.0";
                 }

From 28fd69e873dfdea14531383cda3df541334328e1 Mon Sep 17 00:00:00 2001
From: "mowen@126.com" <mowen@126.com>
Date: Tue, 1 Jun 2021 16:28:39 +0800
Subject: [PATCH 2/5] Add support for sliding / scaling on webview. by passing
 and calculating the relative offset of the reference component's render box

---
 lib/src/base.dart             | 64 ++++++++++++++++--------------
 lib/src/webview_scaffold.dart | 74 +++++++++++++++++++++++++++++++----
 2 files changed, 101 insertions(+), 37 deletions(-)

diff --git a/lib/src/base.dart b/lib/src/base.dart
index 2fdc0384..cc006f28 100644
--- a/lib/src/base.dart
+++ b/lib/src/base.dart
@@ -12,6 +12,12 @@ const _kChannel = 'flutter_webview_plugin';
 // TODO: more general state for iOS/android
 enum WebViewState { shouldStart, startLoad, finishLoad, abortLoad }
 
+/// the transition animation type of page on/off screen
+enum TransitionType{
+  Non,Slide,Scale
+}
+
+
 // TODO: use an id by webview to be able to manage multiple webview
 
 /// Singleton class that communicate with a Webview Instance
@@ -44,7 +50,7 @@ class FlutterWebviewPlugin {
   final _onPostMessage = StreamController<JavascriptMessage>.broadcast();
 
   final Map<String, JavascriptChannel> _javascriptChannels =
-      <String, JavascriptChannel>{};
+  <String, JavascriptChannel>{};
 
   Future<Null> _handleMessages(MethodCall call) async {
     switch (call.method) {
@@ -140,33 +146,33 @@ class FlutterWebviewPlugin {
   /// - [useWideViewPort]: use wide viewport for Android webview ( setUseWideViewPort )
   /// - [ignoreSSLErrors]: use to bypass Android/iOS SSL checks e.g. for self-signed certificates
   Future<void> launch(
-    String url, {
-    Map<String, String>? headers,
-    Set<JavascriptChannel> javascriptChannels = const <JavascriptChannel>{},
-    bool withJavascript = true,
-    bool clearCache = false,
-    bool clearCookies = false,
-    bool mediaPlaybackRequiresUserGesture = true,
-    bool hidden = false,
-    bool enableAppScheme = true,
-    Rect? rect,
-    String? userAgent,
-    bool withZoom = false,
-    bool displayZoomControls = false,
-    bool withLocalStorage = true,
-    bool withLocalUrl = false,
-    String? localUrlScope,
-    bool withOverviewMode = false,
-    bool scrollBar = true,
-    bool supportMultipleWindows = false,
-    bool appCacheEnabled = false,
-    bool allowFileURLs = false,
-    bool useWideViewPort = false,
-    String? invalidUrlRegex,
-    bool geolocationEnabled = false,
-    bool debuggingEnabled = false,
-    bool ignoreSSLErrors = false,
-  }) async {
+      String url, {
+        Map<String, String>? headers,
+        Set<JavascriptChannel> javascriptChannels = const <JavascriptChannel>{},
+        bool withJavascript = true,
+        bool clearCache = false,
+        bool clearCookies = false,
+        bool mediaPlaybackRequiresUserGesture = true,
+        bool hidden = false,
+        bool enableAppScheme = true,
+        Rect? rect,
+        String? userAgent,
+        bool withZoom = false,
+        bool displayZoomControls = false,
+        bool withLocalStorage = true,
+        bool withLocalUrl = false,
+        String? localUrlScope,
+        bool withOverviewMode = false,
+        bool scrollBar = true,
+        bool supportMultipleWindows = false,
+        bool appCacheEnabled = false,
+        bool allowFileURLs = false,
+        bool useWideViewPort = false,
+        String? invalidUrlRegex,
+        bool geolocationEnabled = false,
+        bool debuggingEnabled = false,
+        bool ignoreSSLErrors = false,
+      }) async {
     final args = <String, dynamic>{
       'url': url,
       'withJavascript': withJavascript,
@@ -317,7 +323,7 @@ class FlutterWebviewPlugin {
 
   Set<String> _extractJavascriptChannelNames(Set<JavascriptChannel> channels) {
     final Set<String> channelNames =
-        channels.map((JavascriptChannel channel) => channel.name).toSet();
+    channels.map((JavascriptChannel channel) => channel.name).toSet();
     return channelNames;
   }
 
diff --git a/lib/src/webview_scaffold.dart b/lib/src/webview_scaffold.dart
index 389add90..881d54b8 100644
--- a/lib/src/webview_scaffold.dart
+++ b/lib/src/webview_scaffold.dart
@@ -10,6 +10,8 @@ import 'base.dart';
 class WebviewScaffold extends StatefulWidget {
   const WebviewScaffold({
     Key? key,
+    required this.rrok,
+    required this.margin,
     this.appBar,
     required this.url,
     this.headers,
@@ -41,7 +43,22 @@ class WebviewScaffold extends StatefulWidget {
     this.geolocationEnabled = false,
     this.debuggingEnabled = false,
     this.ignoreSSLErrors = false,
-  }) : super(key: key);
+    this.transitionType = TransitionType.Non
+  })
+      :
+        assert(transitionType != null, 'TransitionType must not null !'),
+        super(key: key);
+
+  // The global key of the referenced render object
+  final GlobalKey rrok;
+
+  final EdgeInsets margin;
+
+  /// in Debug mode,the first init webview page at slide/scale transition,will display misalignment
+  /// after that will be display properly.
+  ///
+  /// in Profile/Release mode, will always display properly.
+  final TransitionType transitionType;
 
   final PreferredSizeWidget? appBar;
   final String url;
@@ -92,6 +109,8 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
     super.initState();
     webviewReference.close();
 
+    perceptionPageTransition();
+
     _onBack = webviewReference.onBack.listen((_) async {
       if (!mounted) {
         return;
@@ -113,13 +132,52 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
     if (widget.hidden) {
       _onStateChanged =
           webviewReference.onStateChanged.listen((WebViewStateChanged state) {
-        if (state.type == WebViewState.finishLoad) {
-          webviewReference.show();
-        }
+            if (state.type == WebViewState.finishLoad) {
+              webviewReference.show();
+            }
+          });
+    }
+  }
+
+  /// 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 (mounted) {
+            driveWebView();
+          }
+        });
       });
     }
   }
 
+  void driveWebView() {
+    final RenderBox box = widget.rrok.currentContext?.findRenderObject()! as RenderBox;
+    final Offset offset = box.localToGlobal(Offset.zero) - Offset(widget.margin.left != null ? widget.margin.left : 0.0, widget.margin.top != null ? widget.margin.top : 0.0);
+    final Size size = box.size;
+    final Rect rect = box.paintBounds;
+
+    final double value = offset.dx / size.width;
+
+    // print('value=$value , offset=$offset , rect=$rect , size=$size , margin=${widget.margin.left}/${widget.margin.top}/${widget.margin.right}/${widget.margin.bottom}');
+
+    switch (widget.transitionType) {
+      case TransitionType.Slide:
+        webviewReference.resize(_rect!.shift(offset));
+        break;
+      case TransitionType.Scale:
+        final double www = size.width * (value * 2);
+        final double hhh = 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;
+    }
+  }
+
   /// Equivalent to [Navigator.of(context)._history.last].
   Route<dynamic> get _topMostRoute {
     Route? topMost;
@@ -161,7 +219,7 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
               clearCache: widget.clearCache,
               clearCookies: widget.clearCookies,
               mediaPlaybackRequiresUserGesture:
-                  widget.mediaPlaybackRequiresUserGesture,
+              widget.mediaPlaybackRequiresUserGesture,
               hidden: widget.hidden,
               enableAppScheme: widget.enableAppScheme,
               userAgent: widget.userAgent,
@@ -217,8 +275,7 @@ class _WebviewPlaceholder extends SingleChildRenderObjectWidget {
   }
 
   @override
-  void updateRenderObject(
-      BuildContext context, _WebviewPlaceholderRender renderObject) {
+  void updateRenderObject(BuildContext context, _WebviewPlaceholderRender renderObject) {
     renderObject..onRectChanged = onRectChanged;
   }
 }
@@ -227,7 +284,8 @@ class _WebviewPlaceholderRender extends RenderProxyBox {
   _WebviewPlaceholderRender({
     RenderBox? child,
     ValueChanged<Rect>? onRectChanged,
-  })  : _callback = onRectChanged,
+  })
+      : _callback = onRectChanged,
         super(child);
 
   ValueChanged<Rect>? _callback;

From fee208858c917360620e3756c69bbe57b3ec8097 Mon Sep 17 00:00:00 2001
From: "mowen@126.com" <mowen@126.com>
Date: Sun, 6 Jun 2021 20:45:37 +0800
Subject: [PATCH 3/5] Update FlutterWebviewPlugin.m

---
 ios/Classes/FlutterWebviewPlugin.m | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ios/Classes/FlutterWebviewPlugin.m b/ios/Classes/FlutterWebviewPlugin.m
index bfd14991..dd7a3d1b 100644
--- a/ios/Classes/FlutterWebviewPlugin.m
+++ b/ios/Classes/FlutterWebviewPlugin.m
@@ -198,7 +198,7 @@ - (void)navigate:(FlutterMethodCall*)call {
             if ( [withLocalUrl boolValue]) {
                 if (@available(iOS 9.0, *)) {
                     NSURL *scopeUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] bundlePath]];
-                    NSURL *fileUrl = [NSURL fileURLWithPaht:[[NSBundle mainBundle] pathForResource:url ofType:nil]];
+                    NSURL *fileUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:url ofType:nil]];
                     [self.webview loadFileURL:fileUrl allowingReadAccessToURL:scopeUrl];
                 } else {
                     @throw @"not available on version earlier than ios 9.0";

From b3d8bd280f83c77300ec984856f6281de35854cf Mon Sep 17 00:00:00 2001
From: "mowen@126.com" <mowen@126.com>
Date: Mon, 7 Jun 2021 13:35:20 +0800
Subject: [PATCH 4/5] u

---
 lib/src/base.dart             | 66 +++++++++++++++++------------------
 lib/src/webview_scaffold.dart | 17 ++-------
 2 files changed, 35 insertions(+), 48 deletions(-)

diff --git a/lib/src/base.dart b/lib/src/base.dart
index cc006f28..2814d811 100644
--- a/lib/src/base.dart
+++ b/lib/src/base.dart
@@ -13,8 +13,9 @@ const _kChannel = 'flutter_webview_plugin';
 enum WebViewState { shouldStart, startLoad, finishLoad, abortLoad }
 
 /// the transition animation type of page on/off screen
-enum TransitionType{
-  Non,Slide,Scale
+enum TransitionType {
+  Non,
+  Slide
 }
 
 
@@ -145,34 +146,33 @@ class FlutterWebviewPlugin {
   /// - [withOverviewMode]: enable overview mode for Android webview ( setLoadWithOverviewMode )
   /// - [useWideViewPort]: use wide viewport for Android webview ( setUseWideViewPort )
   /// - [ignoreSSLErrors]: use to bypass Android/iOS SSL checks e.g. for self-signed certificates
-  Future<void> launch(
-      String url, {
-        Map<String, String>? headers,
-        Set<JavascriptChannel> javascriptChannels = const <JavascriptChannel>{},
-        bool withJavascript = true,
-        bool clearCache = false,
-        bool clearCookies = false,
-        bool mediaPlaybackRequiresUserGesture = true,
-        bool hidden = false,
-        bool enableAppScheme = true,
-        Rect? rect,
-        String? userAgent,
-        bool withZoom = false,
-        bool displayZoomControls = false,
-        bool withLocalStorage = true,
-        bool withLocalUrl = false,
-        String? localUrlScope,
-        bool withOverviewMode = false,
-        bool scrollBar = true,
-        bool supportMultipleWindows = false,
-        bool appCacheEnabled = false,
-        bool allowFileURLs = false,
-        bool useWideViewPort = false,
-        String? invalidUrlRegex,
-        bool geolocationEnabled = false,
-        bool debuggingEnabled = false,
-        bool ignoreSSLErrors = false,
-      }) async {
+  Future<void> launch(String url, {
+    Map<String, String>? headers,
+    Set<JavascriptChannel> javascriptChannels = const <JavascriptChannel>{},
+    bool withJavascript = true,
+    bool clearCache = false,
+    bool clearCookies = false,
+    bool mediaPlaybackRequiresUserGesture = true,
+    bool hidden = false,
+    bool enableAppScheme = true,
+    Rect? rect,
+    String? userAgent,
+    bool withZoom = false,
+    bool displayZoomControls = false,
+    bool withLocalStorage = true,
+    bool withLocalUrl = false,
+    String? localUrlScope,
+    bool withOverviewMode = false,
+    bool scrollBar = true,
+    bool supportMultipleWindows = false,
+    bool appCacheEnabled = false,
+    bool allowFileURLs = false,
+    bool useWideViewPort = false,
+    String? invalidUrlRegex,
+    bool geolocationEnabled = false,
+    bool debuggingEnabled = false,
+    bool ignoreSSLErrors = false,
+  }) async {
     final args = <String, dynamic>{
       'url': url,
       'withJavascript': withJavascript,
@@ -327,8 +327,7 @@ class FlutterWebviewPlugin {
     return channelNames;
   }
 
-  void _handleJavascriptChannelMessage(
-      final String channelName, final String message) {
+  void _handleJavascriptChannelMessage(final String channelName, final String message) {
     if (_javascriptChannels.containsKey(channelName))
       _javascriptChannels[channelName]!
           .onMessageReceived(JavascriptMessage(message));
@@ -336,8 +335,7 @@ class FlutterWebviewPlugin {
       print('Channel "$channelName" is not exstis');
   }
 
-  void _assertJavascriptChannelNamesAreUnique(
-      final Set<JavascriptChannel>? channels) {
+  void _assertJavascriptChannelNamesAreUnique(final Set<JavascriptChannel>? channels) {
     if (channels == null || channels.isEmpty) {
       return;
     }
diff --git a/lib/src/webview_scaffold.dart b/lib/src/webview_scaffold.dart
index 881d54b8..973f0bbc 100644
--- a/lib/src/webview_scaffold.dart
+++ b/lib/src/webview_scaffold.dart
@@ -1,4 +1,5 @@
 import 'dart:async';
+import 'dart:ui';
 
 import 'package:flutter/foundation.dart';
 import 'package:flutter/material.dart';
@@ -154,24 +155,12 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
   }
 
   void driveWebView() {
-    final RenderBox box = widget.rrok.currentContext?.findRenderObject()! as RenderBox;
-    final Offset offset = box.localToGlobal(Offset.zero) - Offset(widget.margin.left != null ? widget.margin.left : 0.0, widget.margin.top != null ? widget.margin.top : 0.0);
-    final Size size = box.size;
-    final Rect rect = box.paintBounds;
-
-    final double value = offset.dx / size.width;
-
-    // print('value=$value , offset=$offset , rect=$rect , size=$size , margin=${widget.margin.left}/${widget.margin.top}/${widget.margin.right}/${widget.margin.bottom}');
-
     switch (widget.transitionType) {
       case TransitionType.Slide:
+        final RenderBox box = widget.rrok.currentContext?.findRenderObject()! as RenderBox;
+        final Offset offset = box.localToGlobal(Offset.zero) - Offset(widget.margin.left != null ? widget.margin.left : 0.0, widget.margin.top != null ? widget.margin.top : 0.0);
         webviewReference.resize(_rect!.shift(offset));
         break;
-      case TransitionType.Scale:
-        final double www = size.width * (value * 2);
-        final double hhh = 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;

From f070d77541fe0d7203ea704b38205cdfe82877da Mon Sep 17 00:00:00 2001
From: "mowen@126.com" <mowen@126.com>
Date: Tue, 29 Jun 2021 06:21:49 +0800
Subject: [PATCH 5/5] Update webview_scaffold.dart

---
 lib/src/webview_scaffold.dart | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/src/webview_scaffold.dart b/lib/src/webview_scaffold.dart
index 973f0bbc..71e8b214 100644
--- a/lib/src/webview_scaffold.dart
+++ b/lib/src/webview_scaffold.dart
@@ -158,7 +158,7 @@ class _WebviewScaffoldState extends State<WebviewScaffold> {
     switch (widget.transitionType) {
       case TransitionType.Slide:
         final RenderBox box = widget.rrok.currentContext?.findRenderObject()! as RenderBox;
-        final Offset offset = box.localToGlobal(Offset.zero) - Offset(widget.margin.left != null ? widget.margin.left : 0.0, widget.margin.top != null ? widget.margin.top : 0.0);
+        final Offset offset = box.localToGlobal(Offset.zero) + Offset(widget.margin.left != null ? widget.margin.left : 0.0, widget.margin.top != null ? widget.margin.top : 0.0);
         webviewReference.resize(_rect!.shift(offset));
         break;
       case TransitionType.Non: