[webview_flutter] Add onUrlChanged callback to platform interface. (#4509)

diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md
index 4c7434a..212f1c4 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md
+++ b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.5.0
+
+* Added `onUrlChanged` callback to platform callback handler.
+
 ## 1.4.0
 
 * Added `loadFile` and `loadHtml` interface methods.
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart
index 8df9f4c..3a6047d 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/method_channel/webview_method_channel.dart
@@ -53,6 +53,9 @@
       case 'onPageStarted':
         _platformCallbacksHandler.onPageStarted(call.arguments['url']!);
         return null;
+      case 'onUrlChanged':
+        _platformCallbacksHandler.onUrlChanged(call.arguments['url']!);
+        return null;
       case 'onWebResourceError':
         _platformCallbacksHandler.onWebResourceError(
           WebResourceError(
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_callbacks_handler.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_callbacks_handler.dart
index 44dae2e..eb97f5b 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_callbacks_handler.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_interface/webview_platform_callbacks_handler.dart
@@ -24,9 +24,16 @@
   void onPageFinished(String url);
 
   /// Invoked by [WebViewPlatformController] when a page is loading.
-  /// /// Only works when [WebSettings.hasProgressTracking] is set to `true`.
+  /// Only works when [WebSettings.hasProgressTracking] is set to `true`.
   void onProgress(int progress);
 
+  /// Invoked by [WebViewPlatformController] when the webview's URL has changed.
+  ///
+  /// Unlike [onPageStarted], [onProgress], and [onPageFinished],
+  /// [onUrlChanged] also fires when navigating without a full page load
+  /// e.g. when navigating within a single page application.
+  void onUrlChanged(String url);
+
   /// Report web resource loading error to the host application.
   void onWebResourceError(WebResourceError error);
 }
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml
index 4a4746d..57ae9a0 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml
+++ b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml
@@ -4,7 +4,7 @@
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview_flutter%22
 # NOTE: We strongly prefer non-breaking changes, even at the expense of a
 # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes
-version: 1.4.0
+version: 1.5.0
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart
index 3960135..f827d8f 100644
--- a/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart
+++ b/packages/webview_flutter/webview_flutter_platform_interface/test/src/method_channel/webview_method_channel_test.dart
@@ -13,7 +13,9 @@
 void main() {
   TestWidgetsFlutterBinding.ensureInitialized();
 
-  group('Tests on `plugin.flutter.io/webview_<channel_id>` channel', () {
+  group(
+      'Tests on `plugin.flutter.io/webview_<channel_id>` channel dart->native',
+      () {
     const int channelId = 1;
     const MethodChannel channel =
         MethodChannel('plugins.flutter.io/webview_$channelId');
@@ -554,6 +556,40 @@
     });
   });
 
+  group(
+      'Tests on `plugin.flutter.io/webview_<channel_id>` channel native->dart',
+      () {
+    const int channelId = 1;
+    final WebViewPlatformCallbacksHandler callbacksHandler =
+        MockWebViewPlatformCallbacksHandler();
+    final JavascriptChannelRegistry javascriptChannelRegistry =
+        MockJavascriptChannelRegistry();
+
+    MethodChannelWebViewPlatform(
+      channelId,
+      callbacksHandler,
+      javascriptChannelRegistry,
+    );
+
+    tearDown(() {
+      reset(callbacksHandler);
+    });
+
+    test('onUrlChanged', () async {
+      // Run
+      await ServicesBinding.instance!.defaultBinaryMessenger
+          .handlePlatformMessage(
+        'plugins.flutter.io/webview_$channelId',
+        StandardMethodCodec()
+            .encodeMethodCall(MethodCall('onUrlChanged', {'url': 'Test Url'})),
+        null,
+      );
+
+      // Verify
+      verify(callbacksHandler.onUrlChanged('Test Url'));
+    });
+  });
+
   group('Tests on `plugins.flutter.io/cookie_manager` channel', () {
     const MethodChannel cookieChannel =
         MethodChannel('plugins.flutter.io/cookie_manager');