diff --git a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md index 7a02dcf024..03f1539a06 100644 --- a/packages/webview_flutter/webview_flutter_android/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_android/CHANGELOG.md @@ -1,3 +1,8 @@ +## 3.9.2 + +* Fixes bug where `PlatformWebViewWidget` doesn't rebuild when the controller or PlatformView + implementation flag changes. + ## 3.9.1 * Adjusts SDK checks for better testability. diff --git a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart index 7c5f0929de..3761dd2b99 100644 --- a/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_android/lib/src/android_webview_controller.dart @@ -744,6 +744,25 @@ class AndroidWebViewWidgetCreationParams /// /// Defaults to false. final bool displayWithHybridComposition; + + @override + int get hashCode => Object.hash( + controller, + layoutDirection, + displayWithHybridComposition, + platformViewsServiceProxy, + instanceManager, + ); + + @override + bool operator ==(Object other) { + return other is AndroidWebViewWidgetCreationParams && + controller == other.controller && + layoutDirection == other.layoutDirection && + displayWithHybridComposition == other.displayWithHybridComposition && + platformViewsServiceProxy == other.platformViewsServiceProxy && + instanceManager == other.instanceManager; + } } /// An implementation of [PlatformWebViewWidget] with the Android WebView API. @@ -763,7 +782,9 @@ class AndroidWebViewWidget extends PlatformWebViewWidget { @override Widget build(BuildContext context) { return PlatformViewLink( - key: _androidParams.key, + // Setting a default key using `params` ensures the `PlatformViewLink` + // recreates the PlatformView when changes are made. + key: _androidParams.key ?? ObjectKey(params), viewType: 'plugins.flutter.io/webview', surfaceFactory: ( BuildContext context, diff --git a/packages/webview_flutter/webview_flutter_android/pubspec.yaml b/packages/webview_flutter/webview_flutter_android/pubspec.yaml index eb79daf79e..f56b5a6a9e 100644 --- a/packages/webview_flutter/webview_flutter_android/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_android/pubspec.yaml @@ -2,7 +2,7 @@ name: webview_flutter_android description: A Flutter plugin that provides a WebView widget on Android. repository: https://github.com/flutter/packages/tree/main/packages/webview_flutter/webview_flutter_android issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+webview%22 -version: 3.9.1 +version: 3.9.2 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart index a6905c7ee9..f3bdb63322 100644 --- a/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter_android/test/android_webview_controller_test.dart @@ -1216,5 +1216,68 @@ void main() { ), ); }); + + testWidgets('PlatformView is recreated when the controller changes', + (WidgetTester tester) async { + final MockPlatformViewsServiceProxy mockPlatformViewsService = + MockPlatformViewsServiceProxy(); + + when( + mockPlatformViewsService.initSurfaceAndroidView( + id: anyNamed('id'), + viewType: anyNamed('viewType'), + layoutDirection: anyNamed('layoutDirection'), + creationParams: anyNamed('creationParams'), + creationParamsCodec: anyNamed('creationParamsCodec'), + onFocus: anyNamed('onFocus'), + ), + ).thenReturn(MockSurfaceAndroidViewController()); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return AndroidWebViewWidget( + AndroidWebViewWidgetCreationParams( + controller: createControllerWithMocks(), + platformViewsServiceProxy: mockPlatformViewsService, + ), + ).build(context); + }, + )); + await tester.pumpAndSettle(); + + verify( + mockPlatformViewsService.initSurfaceAndroidView( + id: anyNamed('id'), + viewType: anyNamed('viewType'), + layoutDirection: anyNamed('layoutDirection'), + creationParams: anyNamed('creationParams'), + creationParamsCodec: anyNamed('creationParamsCodec'), + onFocus: anyNamed('onFocus'), + ), + ); + + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + return AndroidWebViewWidget( + AndroidWebViewWidgetCreationParams( + controller: createControllerWithMocks(), + platformViewsServiceProxy: mockPlatformViewsService, + ), + ).build(context); + }, + )); + await tester.pumpAndSettle(); + + verify( + mockPlatformViewsService.initSurfaceAndroidView( + id: anyNamed('id'), + viewType: anyNamed('viewType'), + layoutDirection: anyNamed('layoutDirection'), + creationParams: anyNamed('creationParams'), + creationParamsCodec: anyNamed('creationParamsCodec'), + onFocus: anyNamed('onFocus'), + ), + ); + }); }); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index f21422537e..35b2b8fb0a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.7.2 + +* Fixes bug where `PlatformWebViewWidget` doesn't rebuild when the controller changes. + ## 3.7.1 * Updates pigeon version to `10.1.4`. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart index 14cba330a7..5b87a39fe0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/webkit_webview_controller.dart @@ -632,6 +632,21 @@ class WebKitWebViewWidgetCreationParams // Maintains instances used to communicate with the native objects they // represent. final InstanceManager _instanceManager; + + @override + int get hashCode => Object.hash( + controller, + layoutDirection, + _instanceManager, + ); + + @override + bool operator ==(Object other) { + return other is WebKitWebViewWidgetCreationParams && + controller == other.controller && + layoutDirection == other.layoutDirection && + _instanceManager == other._instanceManager; + } } /// An implementation of [PlatformWebViewWidget] with the WebKit api. @@ -651,7 +666,9 @@ class WebKitWebViewWidget extends PlatformWebViewWidget { @override Widget build(BuildContext context) { return UiKitView( - key: _webKitParams.key, + // Setting a default key using `params` ensures the `UIKitView` recreates + // the PlatformView when changes are made. + key: _webKitParams.key ?? ObjectKey(params), viewType: 'plugins.flutter.io/webview', onPlatformViewCreated: (_) {}, layoutDirection: params.layoutDirection, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index f213fbc9a5..6e80a39649 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/packages/tree/main/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: 3.7.1 +version: 3.7.2 environment: sdk: ">=2.18.0 <4.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart index 443e3a7163..ae9ab6bd1d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_widget_test.dart @@ -24,37 +24,8 @@ void main() { onWeakReferenceRemoved: (_) {}, ); - final WebKitWebViewController controller = WebKitWebViewController( - WebKitWebViewControllerCreationParams( - webKitProxy: WebKitProxy(createWebView: ( - WKWebViewConfiguration configuration, { - void Function( - String keyPath, - NSObject object, - Map change, - )? observeValue, - InstanceManager? instanceManager, - }) { - final WKWebView webView = WKWebView.detached( - instanceManager: testInstanceManager, - ); - testInstanceManager.addDartCreatedInstance(webView); - return webView; - }, createWebViewConfiguration: ({InstanceManager? instanceManager}) { - return MockWKWebViewConfiguration(); - }, createUIDelegate: ({ - dynamic onCreateWebView, - dynamic requestMediaCapturePermission, - InstanceManager? instanceManager, - }) { - final MockWKUIDelegate mockWKUIDelegate = MockWKUIDelegate(); - when(mockWKUIDelegate.copy()).thenReturn(MockWKUIDelegate()); - - testInstanceManager.addDartCreatedInstance(mockWKUIDelegate); - return mockWKUIDelegate; - }), - ), - ); + final WebKitWebViewController controller = + createTestWebViewController(testInstanceManager); final WebKitWebViewWidget widget = WebKitWebViewWidget( WebKitWebViewWidgetCreationParams( @@ -71,5 +42,86 @@ void main() { expect(find.byType(UiKitView), findsOneWidget); expect(find.byKey(const Key('keyValue')), findsOneWidget); }); + + testWidgets('Key of the PlatformView changes when the controller changes', + (WidgetTester tester) async { + final InstanceManager testInstanceManager = InstanceManager( + onWeakReferenceRemoved: (_) {}, + ); + + // Pump WebViewWidget with first controller. + final WebKitWebViewController controller1 = + createTestWebViewController(testInstanceManager); + final WebKitWebViewWidget webViewWidget = WebKitWebViewWidget( + WebKitWebViewWidgetCreationParams( + controller: controller1, + instanceManager: testInstanceManager, + ), + ); + + await tester.pumpWidget( + Builder( + builder: (BuildContext context) => webViewWidget.build(context), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byKey(ObjectKey(webViewWidget.params)), findsOneWidget); + + // Pump WebViewWidget with second controller. + final WebKitWebViewController controller2 = + createTestWebViewController(testInstanceManager); + final WebKitWebViewWidget webViewWidget2 = WebKitWebViewWidget( + WebKitWebViewWidgetCreationParams( + controller: controller2, + instanceManager: testInstanceManager, + ), + ); + + await tester.pumpWidget( + Builder( + builder: (BuildContext context) => webViewWidget2.build(context), + ), + ); + await tester.pumpAndSettle(); + + expect(find.byKey(ObjectKey(webViewWidget2.params)), findsOneWidget); + }); }); } + +WebKitWebViewController createTestWebViewController( + InstanceManager testInstanceManager, +) { + return WebKitWebViewController( + WebKitWebViewControllerCreationParams( + webKitProxy: WebKitProxy(createWebView: ( + WKWebViewConfiguration configuration, { + void Function( + String keyPath, + NSObject object, + Map change, + )? observeValue, + InstanceManager? instanceManager, + }) { + final WKWebView webView = WKWebView.detached( + instanceManager: testInstanceManager, + ); + testInstanceManager.addDartCreatedInstance(webView); + return webView; + }, createWebViewConfiguration: ({InstanceManager? instanceManager}) { + return MockWKWebViewConfiguration(); + }, createUIDelegate: ({ + dynamic onCreateWebView, + dynamic requestMediaCapturePermission, + InstanceManager? instanceManager, + }) { + final MockWKUIDelegate mockWKUIDelegate = MockWKUIDelegate(); + when(mockWKUIDelegate.copy()).thenReturn(MockWKUIDelegate()); + + testInstanceManager.addDartCreatedInstance(mockWKUIDelegate); + return mockWKUIDelegate; + }), + ), + ); +}