Skip to content

[webview_flutter_android][webview_flutter_wkwebview] Fixes bug where PlatformWebViewWidget doesn't rebuild when the controller changes #4533

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 25, 2023
Merged
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
),
);
});
});
}
Original file line number Diff line number Diff line change
@@ -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`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,37 +24,8 @@ void main() {
onWeakReferenceRemoved: (_) {},
);

final WebKitWebViewController controller = WebKitWebViewController(
WebKitWebViewControllerCreationParams(
webKitProxy: WebKitProxy(createWebView: (
WKWebViewConfiguration configuration, {
void Function(
String keyPath,
NSObject object,
Map<NSKeyValueChangeKey, Object?> 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(
Expand All @@ -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<NSKeyValueChangeKey, Object?> 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;
}),
),
);
}