diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index e911d89ee8d..6193a49719b 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,7 +1,9 @@ -## NEXT +## 4.1.0 +* Adds support to track URL changes. See `NavigationDelegate(onUrlChange)`. * Updates minimum Flutter version to 3.3. * Fixes common typos in tests and documentation. +* Fixes documentation for `WebViewController` and `WebViewCookieManager`. ## 4.0.7 diff --git a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart index 7763327df58..b4d1c7fc009 100644 --- a/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/example/integration_test/webview_flutter_test.dart @@ -706,6 +706,63 @@ Future main() async { final String? currentUrl = await controller.currentUrl(); expect(currentUrl, secondaryUrl); }); + + testWidgets('can receive url changes', (WidgetTester tester) async { + final Completer pageLoaded = Completer(); + + final WebViewController controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate(NavigationDelegate( + onPageFinished: (_) => pageLoaded.complete(), + )) + ..loadRequest(Uri.parse(blankPageEncoded)); + + await tester.pumpWidget(WebViewWidget(controller: controller)); + + await pageLoaded.future; + + final Completer urlChangeCompleter = Completer(); + await controller.setNavigationDelegate(NavigationDelegate( + onUrlChange: (UrlChange change) { + urlChangeCompleter.complete(change.url); + }, + )); + + await controller.runJavaScript('location.href = "$primaryUrl"'); + + await expectLater(urlChangeCompleter.future, completion(primaryUrl)); + }); + + testWidgets('can receive updates to history state', + (WidgetTester tester) async { + final Completer pageLoaded = Completer(); + + final NavigationDelegate navigationDelegate = NavigationDelegate( + onPageFinished: (_) => pageLoaded.complete(), + ); + + final WebViewController controller = WebViewController() + ..setJavaScriptMode(JavaScriptMode.unrestricted) + ..setNavigationDelegate(navigationDelegate) + ..loadRequest(Uri.parse(primaryUrl)); + + await tester.pumpWidget(WebViewWidget(controller: controller)); + + await pageLoaded.future; + + final Completer urlChangeCompleter = Completer(); + await controller.setNavigationDelegate(NavigationDelegate( + onUrlChange: (UrlChange change) { + urlChangeCompleter.complete(change.url); + }, + )); + + await controller.runJavaScript( + 'window.history.pushState({}, "", "secondary.txt");', + ); + + await expectLater(urlChangeCompleter.future, completion(secondaryUrl)); + }); }); testWidgets('target _blank opens in same window', diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index ec1ce4eef16..9cbfc01de56 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -136,6 +136,9 @@ Page resource error: debugPrint('allowing navigation to ${request.url}'); return NavigationDecision.navigate; }, + onUrlChange: (UrlChange change) { + debugPrint('url change to ${change.url}'); + }, ), ) ..addJavaScriptChannel( diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 742e9a7e01b..31e0d28a2b6 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -17,8 +17,8 @@ dependencies: # The example app is bundled with the plugin so we use a path dependency on # the parent directory to use the current plugin's version. path: ../ - webview_flutter_android: ^3.0.0 - webview_flutter_wkwebview: ^3.0.0 + webview_flutter_android: ^3.5.0 + webview_flutter_wkwebview: ^3.3.0 dev_dependencies: build_runner: ^2.1.5 @@ -29,7 +29,7 @@ dev_dependencies: sdk: flutter integration_test: sdk: flutter - webview_flutter_platform_interface: ^2.0.0 + webview_flutter_platform_interface: ^2.1.0 flutter: uses-material-design: true diff --git a/packages/webview_flutter/webview_flutter/example/test/main_test.dart b/packages/webview_flutter/webview_flutter/example/test/main_test.dart index 7857022c14a..b1f36d364ab 100644 --- a/packages/webview_flutter/webview_flutter/example/test/main_test.dart +++ b/packages/webview_flutter/webview_flutter/example/test/main_test.dart @@ -113,4 +113,7 @@ class FakeNavigationDelegate extends PlatformNavigationDelegate { Future setOnWebResourceError( WebResourceErrorCallback onWebResourceError, ) async {} + + @override + Future setOnUrlChange(UrlChangeCallback onUrlChange) async {} } diff --git a/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart b/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart index 3237fa41c0b..9f3923c160e 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/navigation_delegate.dart @@ -36,6 +36,10 @@ import 'webview_controller.dart'; /// ``` class NavigationDelegate { /// Constructs a [NavigationDelegate]. + /// + /// {@template webview_fluttter.navigation_delegate.constructor} + /// `onUrlChange`: invoked when the underlying web view changes to a new url. + /// {@endtemplate} NavigationDelegate({ FutureOr Function(NavigationRequest request)? onNavigationRequest, @@ -43,6 +47,7 @@ class NavigationDelegate { void Function(String url)? onPageFinished, void Function(int progress)? onProgress, void Function(WebResourceError error)? onWebResourceError, + void Function(UrlChange change)? onUrlChange, }) : this.fromPlatformCreationParams( const PlatformNavigationDelegateCreationParams(), onNavigationRequest: onNavigationRequest, @@ -50,6 +55,7 @@ class NavigationDelegate { onPageFinished: onPageFinished, onProgress: onProgress, onWebResourceError: onWebResourceError, + onUrlChange: onUrlChange, ); /// Constructs a [NavigationDelegate] from creation params for a specific @@ -81,6 +87,8 @@ class NavigationDelegate { /// ); /// ``` /// {@endtemplate} + /// + /// {@macro webview_fluttter.navigation_delegate.constructor} NavigationDelegate.fromPlatformCreationParams( PlatformNavigationDelegateCreationParams params, { FutureOr Function(NavigationRequest request)? @@ -89,6 +97,7 @@ class NavigationDelegate { void Function(String url)? onPageFinished, void Function(int progress)? onProgress, void Function(WebResourceError error)? onWebResourceError, + void Function(UrlChange change)? onUrlChange, }) : this.fromPlatform( PlatformNavigationDelegate(params), onNavigationRequest: onNavigationRequest, @@ -96,9 +105,12 @@ class NavigationDelegate { onPageFinished: onPageFinished, onProgress: onProgress, onWebResourceError: onWebResourceError, + onUrlChange: onUrlChange, ); /// Constructs a [NavigationDelegate] from a specific platform implementation. + /// + /// {@macro webview_fluttter.navigation_delegate.constructor} NavigationDelegate.fromPlatform( this.platform, { this.onNavigationRequest, @@ -106,6 +118,7 @@ class NavigationDelegate { this.onPageFinished, this.onProgress, this.onWebResourceError, + void Function(UrlChange change)? onUrlChange, }) { if (onNavigationRequest != null) { platform.setOnNavigationRequest(onNavigationRequest!); @@ -122,6 +135,9 @@ class NavigationDelegate { if (onWebResourceError != null) { platform.setOnWebResourceError(onWebResourceError!); } + if (onUrlChange != null) { + platform.setOnUrlChange(onUrlChange); + } } /// Implementation of [PlatformNavigationDelegate] for the current platform. diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart index 014514d0979..fc5e380b552 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart @@ -53,7 +53,7 @@ class WebViewController { /// Constructs a [WebViewController] from creation params for a specific /// platform. /// - /// {@template webview_flutter.WebViewCookieManager.fromPlatformCreationParams} + /// {@template webview_flutter.WebViewController.fromPlatformCreationParams} /// Below is an example of setting platform-specific creation parameters for /// iOS and Android: /// diff --git a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart index 112966d4776..4c14dcd4ef4 100644 --- a/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/webview_flutter/lib/webview_flutter.dart @@ -18,6 +18,7 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte PlatformWebViewCookieManagerCreationParams, PlatformWebViewWidgetCreationParams, ProgressCallback, + UrlChange, WebResourceError, WebResourceErrorCallback, WebResourceErrorType, diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 8c013dec231..85846a18e40 100644 --- a/packages/webview_flutter/webview_flutter/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter description: A Flutter plugin that provides a WebView widget on Android and iOS. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 4.0.7 +version: 4.1.0 environment: sdk: ">=2.18.0 <4.0.0" @@ -20,7 +20,7 @@ dependencies: flutter: sdk: flutter webview_flutter_android: ^3.0.0 - webview_flutter_platform_interface: ^2.0.0 + webview_flutter_platform_interface: ^2.1.0 webview_flutter_wkwebview: ^3.0.0 dev_dependencies: diff --git a/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart b/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart index 839454eaa60..ffd97718bd4 100644 --- a/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart +++ b/packages/webview_flutter/webview_flutter/test/navigation_delegate_test.dart @@ -75,6 +75,18 @@ void main() { verify(delegate.platform.setOnWebResourceError(onWebResourceError)); }); + + test('onUrlChange', () async { + WebViewPlatform.instance = TestWebViewPlatform(); + + void onUrlChange(UrlChange change) {} + + final NavigationDelegate delegate = NavigationDelegate( + onUrlChange: onUrlChange, + ); + + verify(delegate.platform.setOnUrlChange(onUrlChange)); + }); }); } diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart index 68b60ec8289..32f30b159a4 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -41,6 +41,8 @@ void main() { main_file.WebViewCookie; // ignore: unnecessary_statements main_file.WebResourceErrorType; + // ignore: unnecessary_statements + main_file.UrlChange; }); }); }