diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 4c7434a86b41..212f1c4c4784 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 8df9f4c62b33..3a6047d9d5d0 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 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController { 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 44dae2ece434..fd2df5fdde64 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 @@ abstract class WebViewPlatformCallbacksHandler { 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 using 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 4a4746d8ab68..57ae9a06f056 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 @@ repository: https://github.com/flutter/plugins/tree/master/packages/webview_flut 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 396013535aa9..f827d8fcf6b0 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 @@ import 'package:webview_flutter_platform_interface/webview_flutter_platform_inte void main() { TestWidgetsFlutterBinding.ensureInitialized(); - group('Tests on `plugin.flutter.io/webview_` channel', () { + group( + 'Tests on `plugin.flutter.io/webview_` channel dart->native', + () { const int channelId = 1; const MethodChannel channel = MethodChannel('plugins.flutter.io/webview_$channelId'); @@ -554,6 +556,40 @@ void main() { }); }); + group( + 'Tests on `plugin.flutter.io/webview_` 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'); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index cbf1f04a6181..b6080fd66d3d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.5.0 + +* Support new `onUrlChanged` event in platform interface. + ## 2.4.0 * Implemented new `loadFile` and `loadHtmlString` methods from the platform interface. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m index a819a9b53d60..e35a1317d91a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWKNavigationDelegateTests.m @@ -69,4 +69,38 @@ - (void)testWebViewWebEvaluateJavaScriptShouldNotBeCalledWhenShouldEnableZoomIsT [self.navigationDelegate webView:webview didFinishNavigation:navigation]; } +- (void)testWebViewObserveValueForKeyPathCallsMethodChannelOnURLChange { + [self.navigationDelegate + observeValueForKeyPath:@"URL" + ofObject:nil + change:@{ + NSKeyValueChangeNewKey : [NSURL URLWithString:@"https://flutter.dev/"] + } + context:nil]; + OCMVerify([self.mockMethodChannel + invokeMethod:@"onUrlChanged" + arguments:[OCMArg isEqual:@{@"url" : @"https://flutter.dev/"}]]); +} + +- (void)testWebViewObserveValueForKeyPathReturnsForNonURLChanges { + [self.navigationDelegate + observeValueForKeyPath:@"IRRELEVANT_PATH" + ofObject:nil + change:@{ + NSKeyValueChangeNewKey : [NSURL URLWithString:@"https://flutter.dev/"] + } + context:nil]; + + OCMReject([self.mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +} + +- (void)testWebViewObserveValueForKeyPathReturnsForNSNullURLChanges { + [self.navigationDelegate observeValueForKeyPath:@"URL" + ofObject:nil + change:@{NSKeyValueChangeNewKey : [NSNull null]} + context:nil]; + + OCMReject([self.mockMethodChannel invokeMethod:[OCMArg any] arguments:[OCMArg any]]); +} + @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index ab4b77336765..17db13362023 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart @@ -35,6 +35,9 @@ typedef void PageFinishedCallback(String url); /// Signature for when a [WebView] is loading a page. typedef void PageLoadingCallback(int progress); +/// Signature for when a [WebView] changed its current URL. +typedef void UrlChangedCallback(String url); + /// Signature for when a [WebView] has failed to load a resource. typedef void WebResourceErrorCallback(WebResourceError error); @@ -61,6 +64,7 @@ class WebView extends StatefulWidget { this.onPageStarted, this.onPageFinished, this.onProgress, + this.onUrlChanged, this.onWebResourceError, this.debuggingEnabled = false, this.gestureNavigationEnabled = false, @@ -175,6 +179,9 @@ class WebView extends StatefulWidget { /// Invoked when a page is loading. final PageLoadingCallback? onProgress; + /// Invoked when a webview's URL has changed. + final UrlChangedCallback? onUrlChanged; + /// Invoked when a web resource has failed to load. /// /// This callback is only called for the main page. @@ -645,6 +652,13 @@ class _PlatformCallbacksHandler implements WebViewPlatformCallbacksHandler { } } + @override + void onUrlChanged(String url) { + if (_webView.onUrlChanged != null) { + _webView.onUrlChanged!(url); + } + } + void onWebResourceError(WebResourceError error) { if (_webView.onWebResourceError != null) { _webView.onWebResourceError!(error); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m index 125d3cabdcf1..c0233920d586 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FLTWKNavigationDelegate.m @@ -16,6 +16,19 @@ - (instancetype)initWithChannel:(FlutterMethodChannel *)channel { return self; } +- (void)observeValueForKeyPath:(NSString *)keyPath + ofObject:(id)object + change:(NSDictionary *)change + context:(void *)context { + if (![keyPath isEqualToString:@"URL"]) { + return; + } + NSURL *newURL = [change objectForKey:NSKeyValueChangeNewKey]; + if (![newURL isEqual:[NSNull null]]) { + [_methodChannel invokeMethod:@"onUrlChanged" arguments:@{@"url" : newURL.absoluteString}]; + } +} + #pragma mark - WKNavigationDelegate conformance - (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 351d1ae58760..23c496395dc9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -110,6 +110,10 @@ - (instancetype)initWithFrame:(CGRect)frame _webView.scrollView.automaticallyAdjustsScrollIndicatorInsets = NO; } } + [_webView addObserver:_navigationDelegate + forKeyPath:@"URL" + options:NSKeyValueObservingOptionNew + context:NULL]; [self applySettings:settings]; // TODO(amirh): return an error if apply settings failed once it's possible to do so. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 466c1a2a4fcd..4ae2820522ce 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_wkwebview description: A Flutter plugin that provides a WebView widget based on Apple's WKWebView control. repository: https://github.com/flutter/plugins/tree/master/packages/webview_flutter/webview_flutter_wkwebview issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 2.4.0 +version: 2.5.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -18,7 +18,9 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_platform_interface: ^1.3.0 + #TODO (BeMacized): CHANGE TO VERSION DEPENDENCY ONCE UPDATED PLATFORM INTERFACE HAS BEEN PUBLISHED. + webview_flutter_platform_interface: + path: ../webview_flutter_platform_interface dev_dependencies: flutter_driver: