diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 9f8374332e9d..ff75e7374c3c 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 4.4.2 + +* Adds support for the `allowsLinkPreview` property on iOS. + ## 4.4.1 * Exposes `JavaScriptLogLevel` from platform interface. diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 3133daf588c6..a1e01f74d7bf 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -29,6 +29,11 @@ dev_dependencies: sdk: flutter webview_flutter_platform_interface: ^2.3.0 +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_platform_interface: + path: ../../webview_flutter_platform_interface + flutter: uses-material-design: true assets: diff --git a/packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart index 55826a533b17..a74352f922bd 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/legacy/webview.dart @@ -92,6 +92,7 @@ class WebView extends StatefulWidget { this.onWebResourceError, this.debuggingEnabled = false, this.gestureNavigationEnabled = false, + this.allowsLinkPreview = true, this.userAgent, this.zoomEnabled = true, this.initialMediaPlaybackPolicy = @@ -260,6 +261,13 @@ class WebView extends StatefulWidget { /// By default `gestureNavigationEnabled` is false. final bool gestureNavigationEnabled; + /// Whether pressing a link displays a preview of the destination for the link. + /// + /// Not supported on all platforms. + /// + /// By default `allowsLinkPreview` is true. + final bool allowsLinkPreview; + /// The value used for the HTTP User-Agent: request header. /// /// When null the platform's webview default is used for the User-Agent header. @@ -380,6 +388,7 @@ WebSettings _webSettingsFromWidget(WebView widget) { hasProgressTracking: widget.onProgress != null, debuggingEnabled: widget.debuggingEnabled, gestureNavigationEnabled: widget.gestureNavigationEnabled, + allowsLinkPreview: widget.allowsLinkPreview, allowsInlineMediaPlayback: widget.allowsInlineMediaPlayback, userAgent: WebSetting.of(widget.userAgent), zoomEnabled: widget.zoomEnabled, @@ -397,6 +406,7 @@ WebSettings _clearUnchangedWebSettings( assert(newValue.hasNavigationDelegate != null); assert(newValue.debuggingEnabled != null); assert(newValue.zoomEnabled != null); + assert(newValue.allowsLinkPreview != null); JavascriptMode? javascriptMode; bool? hasNavigationDelegate; @@ -404,6 +414,7 @@ WebSettings _clearUnchangedWebSettings( bool? debuggingEnabled; WebSetting userAgent = const WebSetting.absent(); bool? zoomEnabled; + bool? allowsLinkPreview; if (currentValue.javascriptMode != newValue.javascriptMode) { javascriptMode = newValue.javascriptMode; } @@ -422,6 +433,9 @@ WebSettings _clearUnchangedWebSettings( if (currentValue.zoomEnabled != newValue.zoomEnabled) { zoomEnabled = newValue.zoomEnabled; } + if (currentValue.allowsLinkPreview != newValue.allowsLinkPreview) { + allowsLinkPreview = newValue.allowsLinkPreview; + } return WebSettings( javascriptMode: javascriptMode, @@ -430,6 +444,7 @@ WebSettings _clearUnchangedWebSettings( debuggingEnabled: debuggingEnabled, userAgent: userAgent, zoomEnabled: zoomEnabled, + allowsLinkPreview: allowsLinkPreview, ); } 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 0b81978d6fcc..1beec24891e1 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview_controller.dart @@ -354,6 +354,11 @@ class WebViewController { return platform.setUserAgent(userAgent); } + /// Whether to display a preview of the destination for the link + Future setAllowsLinkPreview(bool allow) { + return platform.setAllowsLinkPreview(allow); + } + /// Sets a callback that notifies the host application on any log messages /// written to the JavaScript console. /// diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 75b9efcc23a4..21ffe07f2e6e 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.4.1 +version: 4.4.2 environment: sdk: ">=2.19.0 <4.0.0" @@ -23,6 +23,11 @@ dependencies: webview_flutter_platform_interface: ^2.6.0 webview_flutter_wkwebview: ^3.9.0 +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_platform_interface: + path: ../webview_flutter_platform_interface + dev_dependencies: build_runner: ^2.1.5 flutter_test: diff --git a/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart b/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart index 4db70113dfb2..cedb5f62c128 100644 --- a/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/legacy/webview_flutter_test.dart @@ -1114,6 +1114,58 @@ void main() { }); }); + group('allowsLinkPreview', () { + testWidgets('defaults to true', (WidgetTester tester) async { + await tester.pumpWidget(const WebView()); + + final CreationParams params = captureBuildArgs( + mockWebViewPlatform, + creationParams: true, + ).single as CreationParams; + + expect(params.webSettings!.allowsLinkPreview, true); + }); + + testWidgets('can be disabled', (WidgetTester tester) async { + await tester.pumpWidget(const WebView( + allowsLinkPreview: false, + )); + + final CreationParams params = captureBuildArgs( + mockWebViewPlatform, + creationParams: true, + ).single as CreationParams; + + expect(params.webSettings!.allowsLinkPreview, false); + }); + + testWidgets('can be changed', (WidgetTester tester) async { + final GlobalKey key = GlobalKey(); + await tester.pumpWidget(WebView(key: key)); + + await tester.pumpWidget(WebView( + key: key, + allowsLinkPreview: false, + )); + + final WebSettings enabledSettings = + verify(mockWebViewPlatformController.updateSettings(captureAny)) + .captured + .last as WebSettings; + expect(enabledSettings.allowsLinkPreview, false); + + await tester.pumpWidget(WebView( + key: key, + )); + + final WebSettings disabledSettings = + verify(mockWebViewPlatformController.updateSettings(captureAny)) + .captured + .last as WebSettings; + expect(disabledSettings.allowsLinkPreview, true); + }); + }); + group('Custom platform implementation', () { setUp(() { WebView.platform = MyWebViewPlatform(); @@ -1143,6 +1195,7 @@ void main() { debuggingEnabled: false, userAgent: const WebSetting.of(null), gestureNavigationEnabled: true, + allowsLinkPreview: true, zoomEnabled: true, ), ))); @@ -1326,6 +1379,7 @@ class MatchesWebSettings extends Matcher { _webSettings!.gestureNavigationEnabled == webSettings.gestureNavigationEnabled && _webSettings!.userAgent == webSettings.userAgent && + _webSettings!.allowsLinkPreview == webSettings.allowsLinkPreview && _webSettings!.zoomEnabled == webSettings.zoomEnabled; } } diff --git a/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart b/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart index 695a781738e2..1ef63898fda3 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_controller_test.dart @@ -336,6 +336,18 @@ void main() { ); }); + test('setAllowsLinkPreview', () async { + final MockPlatformWebViewController mockPlatformWebViewController = + MockPlatformWebViewController(); + + final WebViewController webViewController = WebViewController.fromPlatform( + mockPlatformWebViewController, + ); + + await webViewController.setAllowsLinkPreview(false); + verify(mockPlatformWebViewController.setAllowsLinkPreview(false)); + }); + test('setUserAgent', () async { final MockPlatformWebViewController mockPlatformWebViewController = MockPlatformWebViewController(); diff --git a/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart index 343a7ad56892..922abe69416b 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_controller_test.mocks.dart @@ -343,6 +343,17 @@ class MockPlatformWebViewController extends _i1.Mock returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + + @override + _i5.Future setAllowsLinkPreview(bool? allow) => (super.noSuchMethod( + Invocation.method( + #setAllowsLinkPreview, + [allow], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override _i5.Future setOnPlatformPermissionRequest( void Function(_i2.PlatformWebViewPermissionRequest)? diff --git a/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart index aeff78083714..f5c1360d6e7c 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_widget_test.mocks.dart @@ -361,6 +361,17 @@ class MockPlatformWebViewController extends _i1.Mock returnValue: _i7.Future.value(), returnValueForMissingStub: _i7.Future.value(), ) as _i7.Future); + + @override + _i7.Future setAllowsLinkPreview(bool? allow) => (super.noSuchMethod( + Invocation.method( + #setAllowsLinkPreview, + [allow], + ), + returnValue: _i7.Future.value(), + returnValueForMissingStub: _i7.Future.value(), + ) as _i7.Future); + @override _i7.Future setOnPlatformPermissionRequest( void Function(_i2.PlatformWebViewPermissionRequest)? diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 2748ea394cc3..5fca7ca983ab 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 @@ +## 2.6.1 + +* Adds support for the `allowsLinkPreview` property on iOS. + ## 2.6.0 * Adds support to register a callback to intercept messages that are written to diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/legacy/types/web_settings.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/legacy/types/web_settings.dart index 91f0768e38cb..ac0cf6036f4d 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/legacy/types/web_settings.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/legacy/types/web_settings.dart @@ -82,6 +82,7 @@ class WebSettings { this.hasProgressTracking, this.debuggingEnabled, this.gestureNavigationEnabled, + this.allowsLinkPreview, this.allowsInlineMediaPlayback, this.zoomEnabled, required this.userAgent, @@ -125,8 +126,13 @@ class WebSettings { /// See also: [WebView.gestureNavigationEnabled] final bool? gestureNavigationEnabled; + /// Determines whether pressing a link displays a preview of the destination for the link in iOS. + /// + /// See also: [WebView.allowsLinkPreview] + final bool? allowsLinkPreview; + @override String toString() { - return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, hasProgressTracking: $hasProgressTracking, debuggingEnabled: $debuggingEnabled, gestureNavigationEnabled: $gestureNavigationEnabled, userAgent: $userAgent, allowsInlineMediaPlayback: $allowsInlineMediaPlayback)'; + return 'WebSettings(javascriptMode: $javascriptMode, hasNavigationDelegate: $hasNavigationDelegate, hasProgressTracking: $hasProgressTracking, debuggingEnabled: $debuggingEnabled, gestureNavigationEnabled: $gestureNavigationEnabled, allowsLinkPreview: $allowsLinkPreview, userAgent: $userAgent, allowsInlineMediaPlayback: $allowsInlineMediaPlayback)'; } } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_webview_controller.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_webview_controller.dart index 806d2a7a6b66..a84cd0aa1aab 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_webview_controller.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/platform_webview_controller.dart @@ -261,6 +261,11 @@ abstract class PlatformWebViewController extends PlatformInterface { 'setUserAgent is not implemented on the current platform'); } + /// Whether to display a preview of the destination for the link + /// + /// This is not supported by all platforms, so it defaults to a noop + Future setAllowsLinkPreview(bool allow) async {} + /// Sets a callback that notifies the host application that web content is /// requesting permission to access the specified resources. Future setOnPlatformPermissionRequest( diff --git a/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 71e85f32ded2..cdebcc8a6064 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/packages/tree/main/packages/webview_flutt 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: 2.6.0 +version: 2.6.1 environment: sdk: ">=2.19.0 <4.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 197c5d70784f..f39dd627900e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.9.3 + +* Adds support for the `allowsLinkPreview` property on iOS. + ## 3.9.2 * Fixes error caused by calling `WKWebViewConfiguration.limitsNavigationsToAppBoundDomains` on diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m index 617f7309cdb5..6ba41c61c5a3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FWFWebViewHostApiTests.m @@ -353,6 +353,22 @@ - (void)testSetAllowsBackForwardNavigationGestures { XCTAssertNil(error); } +- (void)testSetAllowsLinkPreview { + FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); + + FWFInstanceManager *instanceManager = [[FWFInstanceManager alloc] init]; + [instanceManager addDartCreatedInstance:mockWebView withIdentifier:0]; + + FWFWebViewHostApiImpl *hostAPI = [[FWFWebViewHostApiImpl alloc] + initWithBinaryMessenger:OCMProtocolMock(@protocol(FlutterBinaryMessenger)) + instanceManager:instanceManager]; + + FlutterError *error; + [hostAPI setAllowsLinkPreviewForWebViewWithIdentifier:@0 isAllowed:@NO error:&error]; + OCMVerify([mockWebView setAllowsLinkPreview:NO]); + XCTAssertNil(error); +} + - (void)testEvaluateJavaScript { FWFWebView *mockWebView = OCMClassMock([FWFWebView class]); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/legacy/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/legacy/web_view.dart index afb5f2065616..ca116c624ae3 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/legacy/web_view.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/legacy/web_view.dart @@ -68,6 +68,7 @@ class WebView extends StatefulWidget { this.onWebResourceError, this.debuggingEnabled = false, this.gestureNavigationEnabled = false, + this.allowsLinkPreview = true, this.userAgent, this.zoomEnabled = true, this.initialMediaPlaybackPolicy = @@ -205,6 +206,13 @@ class WebView extends StatefulWidget { /// By default `gestureNavigationEnabled` is false. final bool gestureNavigationEnabled; + /// Whether pressing a link displays a preview of the destination for the link. + /// + /// Not supported on all platforms. + /// + /// By default `allowsLinkPreview` is true, to match the default on iOS. + final bool allowsLinkPreview; + /// The value used for the HTTP User-Agent: request header. /// /// When null the platform's webview default is used for the User-Agent header. @@ -618,6 +626,7 @@ WebSettings _webSettingsFromWidget(WebView widget) { hasProgressTracking: widget.onProgress != null, debuggingEnabled: widget.debuggingEnabled, gestureNavigationEnabled: widget.gestureNavigationEnabled, + allowsLinkPreview: widget.allowsLinkPreview, allowsInlineMediaPlayback: widget.allowsInlineMediaPlayback, userAgent: WebSetting.of(widget.userAgent), zoomEnabled: widget.zoomEnabled, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 11f6bc6092fa..85e92f34e28b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -25,6 +25,11 @@ dev_dependencies: integration_test: sdk: flutter +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_platform_interface: + path: ../../webview_flutter_platform_interface + flutter: uses-material-design: true assets: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h index 11c49d24b541..70061955df56 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -744,6 +744,12 @@ NSObject *FWFWKWebViewHostApiGetCodec(void); - (void)setAllowsBackForwardForWebViewWithIdentifier:(NSNumber *)identifier isAllowed:(NSNumber *)allow error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setAllowsLinkPreviewForWebViewWithIdentifier:(NSNumber *)identifier + isAllowed:(NSNumber *)allow + error:(FlutterError *_Nullable *_Nonnull)error; +- (void)setUserAgentForWebViewWithIdentifier:(NSNumber *)identifier + userAgent:(nullable NSString *)userAgent + error:(FlutterError *_Nullable *_Nonnull)error; - (void)setCustomUserAgentForWebViewWithIdentifier:(NSNumber *)identifier userAgent:(nullable NSString *)userAgent error:(FlutterError *_Nullable *_Nonnull)error; diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m index cc58067418e9..0cb26fa5bf2c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -2459,6 +2459,32 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName: + @"dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsLinkPreview" + binaryMessenger:binaryMessenger + codec:FWFWKWebViewHostApiGetCodec()]; + if (api) { + NSCAssert([api respondsToSelector:@selector + (setAllowsLinkPreviewForWebViewWithIdentifier:isAllowed:error:)], + @"FWFWKWebViewHostApi api (%@) doesn't respond to " + @"@selector(setAllowsLinkPreviewForWebViewWithIdentifier:isAllowed:error:)", + api); + [channel setMessageHandler:^(id _Nullable message, FlutterReply callback) { + NSArray *args = message; + NSNumber *arg_identifier = GetNullableObjectAtIndex(args, 0); + NSNumber *arg_allow = GetNullableObjectAtIndex(args, 1); + FlutterError *error; + [api setAllowsLinkPreviewForWebViewWithIdentifier:arg_identifier + isAllowed:arg_allow + error:&error]; + callback(wrapResult(nil, error)); + }]; + } else { + [channel setMessageHandler:nil]; + } + } { FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] initWithName: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m index eb30b773d1f2..b7bd6ecc43b5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -275,6 +275,14 @@ - (void)reloadWebViewWithIdentifier:(nonnull NSNumber *)identifier [[self webViewForIdentifier:identifier] setAllowsBackForwardNavigationGestures:allow.boolValue]; } +- (void) + setAllowsLinkPreviewForWebViewWithIdentifier:(nonnull NSNumber *)identifier + isAllowed:(nonnull NSNumber *)allow + error:(FlutterError *_Nullable __autoreleasing *_Nonnull) + error { + [[self webViewForIdentifier:identifier] setAllowsLinkPreview:allow.boolValue]; +} + - (void) setNavigationDelegateForWebViewWithIdentifier:(nonnull NSNumber *)identifier delegateIdentifier:(nullable NSNumber *)navigationDelegateIdentifier diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart index b0eaadeb739f..f6d9ee0035c9 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.g.dart @@ -2513,6 +2513,29 @@ class WKWebViewHostApi { } } + Future setAllowsLinkPreview(int arg_identifier, bool arg_allow) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsLinkPreview', + codec, + binaryMessenger: _binaryMessenger); + final List? replyList = await channel + .send([arg_identifier, arg_allow]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } + Future setCustomUserAgent( int arg_identifier, String? arg_userAgent) async { final BasicMessageChannel channel = BasicMessageChannel( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart index f344edb00bb6..1a81996a0e0b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/legacy/web_kit_webview_widget.dart @@ -422,6 +422,10 @@ class WebKitWebViewPlatformController extends WebViewPlatformController { webView.setAllowsBackForwardNavigationGestures( setting.gestureNavigationEnabled!, ), + if (setting.allowsLinkPreview != null) + webView.setAllowsLinkPreview( + setting.allowsLinkPreview!, + ), ]); } diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index db9f41c28519..41b84316c1bf 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart @@ -1087,6 +1087,18 @@ class WKWebView extends UIView { ); } + /// Whether pressing a link displays a preview of the destination for the link. + /// + /// In iOS, this property is available on devices that support 3D Touch. In iOS 10 and later, the default value is true; in previous versions of iOS, the default value is false. + /// + /// Sets [WKWebView.allowsLinkPreview](https://developer.apple.com/documentation/webkit/wkwebview/1415000-allowslinkpreview?language=objc). + Future setAllowsLinkPreview(bool allow) { + return _webViewApi.setAllowsLinkPreviewForInstances( + this, + allow, + ); + } + /// The custom user agent string. /// /// The default value of this property is null. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart index ee545d45b718..1f057fc08c42 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit_api_impls.dart @@ -1031,6 +1031,17 @@ class WKWebViewHostApiImpl extends WKWebViewHostApi { ); } + /// Calls [setAllowsLinkPreview] with the ids of the provided object instances. + Future setAllowsLinkPreviewForInstances( + WKWebView instance, + bool allow, + ) { + return setAllowsLinkPreview( + instanceManager.getIdentifier(instance)!, + allow, + ); + } + /// Calls [setCustomUserAgent] with the ids of the provided object instances. Future setCustomUserAgentForInstances( WKWebView instance, 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 d15b28251828..7348ff857d78 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 @@ -485,6 +485,11 @@ class WebKitWebViewController extends PlatformWebViewController { return _webView.setCustomUserAgent(userAgent); } + @override + Future setAllowsLinkPreview(bool allow) { + return _webView.setAllowsLinkPreview(allow); + } + @override Future enableZoom(bool enabled) async { if (_zoomEnabled == enabled) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index 8e9e16ff425d..4d0e12c4a435 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -688,6 +688,9 @@ abstract class WKWebViewHostApi { @ObjCSelector('setAllowsBackForwardForWebViewWithIdentifier:isAllowed:') void setAllowsBackForwardNavigationGestures(int identifier, bool allow); + @ObjCSelector('setAllowsLinkPreviewForWebViewWithIdentifier:isAllowed:') + void setAllowsLinkPreview(int identifier, bool allow); + @ObjCSelector('setCustomUserAgentForWebViewWithIdentifier:userAgent:') void setCustomUserAgent(int identifier, String? userAgent); diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 2d469bd83825..4b7bb23eaeb4 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.9.2 +version: 3.9.3 environment: sdk: ">=2.19.0 <4.0.0" @@ -29,6 +29,11 @@ dev_dependencies: mockito: 5.4.1 pigeon: ^10.1.4 +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_platform_interface: + path: ../webview_flutter_platform_interface + topics: - html - webview diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart index 8de47910fef3..c7a55733807c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/legacy/web_kit_webview_widget_test.mocks.dart @@ -624,6 +624,17 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: _i5.Future.value(), returnValueForMissingStub: _i5.Future.value(), ) as _i5.Future); + + @override + _i5.Future setAllowsLinkPreview(bool? allow) => (super.noSuchMethod( + Invocation.method( + #setAllowsLinkPreview, + [allow], + ), + returnValue: _i5.Future.value(), + returnValueForMissingStub: _i5.Future.value(), + ) as _i5.Future); + @override _i5.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod( Invocation.method( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart index 03e5b6cbca5e..77e646262957 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.g.dart @@ -1154,6 +1154,8 @@ abstract class TestWKWebViewHostApi { void setAllowsBackForwardNavigationGestures(int identifier, bool allow); + void setAllowsLinkPreview(int identifier, bool allow); + void setCustomUserAgent(int identifier, String? userAgent); Future evaluateJavaScript(int identifier, String javaScriptString); @@ -1558,6 +1560,32 @@ abstract class TestWKWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsLinkPreview', + codec, + binaryMessenger: binaryMessenger); + if (api == null) { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, null); + } else { + _testBinaryMessengerBinding!.defaultBinaryMessenger + .setMockDecodedMessageHandler(channel, + (Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsLinkPreview was null.'); + final List args = (message as List?)!; + final int? arg_identifier = (args[0] as int?); + assert(arg_identifier != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsLinkPreview was null, expected non-null int.'); + final bool? arg_allow = (args[1] as bool?); + assert(arg_allow != null, + 'Argument for dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setAllowsLinkPreview was null, expected non-null bool.'); + api.setAllowsLinkPreview(arg_identifier!, arg_allow!); + return []; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.webview_flutter_wkwebview.WKWebViewHostApi.setCustomUserAgent', diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart index 81ef6a8ca9d6..d6bb4406e9f1 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/ui_kit/ui_kit_test.mocks.dart @@ -293,6 +293,23 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); + + @override + void setAllowsLinkPreview( + int? identifier, + bool? allow, + ) => + super.noSuchMethod( + Invocation.method( + #setAllowsLinkPreview, + [ + identifier, + allow, + ], + ), + returnValueForMissingStub: null, + ); + @override void setCustomUserAgent( int? identifier, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart index 98a70de7b328..6e644ff2eee0 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.dart @@ -833,6 +833,14 @@ void main() { )); }); + test('setAllowsLinkPreview', () { + webView.setAllowsLinkPreview(false); + verify(mockPlatformHostApi.setAllowsLinkPreview( + webViewInstanceId, + false, + )); + }); + test('setCustomUserAgent', () { webView.setCustomUserAgent('hello'); verify(mockPlatformHostApi.setCustomUserAgent( diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart index 2343af2f17ff..eb451697b9d2 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit/web_kit_test.mocks.dart @@ -522,6 +522,23 @@ class MockTestWKWebViewHostApi extends _i1.Mock ), returnValueForMissingStub: null, ); + + @override + void setAllowsLinkPreview( + int? identifier, + bool? allow, + ) => + super.noSuchMethod( + Invocation.method( + #setAllowsLinkPreview, + [ + identifier, + allow, + ], + ), + returnValueForMissingStub: null, + ); + @override void setCustomUserAgent( int? identifier, diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart index 08bd48041606..24f06f9f111d 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/webkit_webview_controller_test.mocks.dart @@ -732,6 +732,17 @@ class MockWKWebView extends _i1.Mock implements _i5.WKWebView { returnValue: _i6.Future.value(), returnValueForMissingStub: _i6.Future.value(), ) as _i6.Future); + + @override + _i6.Future setAllowsLinkPreview(bool? allow) => (super.noSuchMethod( + Invocation.method( + #setAllowsLinkPreview, + [allow], + ), + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); + @override _i6.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod( Invocation.method(