diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 73dd7ced0279..b30eb7acf43b 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 3.0.5 +* Adds support for the `allowsLinkPreview` property on iOS. * Updates minimum Flutter version to 2.10. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Ignores unnecessary import warnings in preparation for [upcoming Flutter changes](https://github.com/flutter/flutter/pull/104231). diff --git a/packages/webview_flutter/webview_flutter/example/pubspec.yaml b/packages/webview_flutter/webview_flutter/example/pubspec.yaml index 7af8b2f9dcc4..a1e3da2903ea 100644 --- a/packages/webview_flutter/webview_flutter/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter/example/pubspec.yaml @@ -18,6 +18,15 @@ dependencies: # the parent directory to use the current plugin's version. path: ../ +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_android: + path: ../../webview_flutter_android + webview_flutter_platform_interface: + path: ../../webview_flutter_platform_interface + webview_flutter_wkwebview: + path: ../../webview_flutter_wkwebview + dev_dependencies: espresso: ^0.1.0+2 flutter_driver: diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index 697eb487b953..fdcaafb6f621 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -90,6 +90,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, @@ -399,6 +408,7 @@ WebSettings _clearUnchangedWebSettings( assert(newValue.debuggingEnabled != null); assert(newValue.userAgent != null); assert(newValue.zoomEnabled != null); + assert(newValue.allowsLinkPreview != null); JavascriptMode? javascriptMode; bool? hasNavigationDelegate; @@ -406,6 +416,8 @@ WebSettings _clearUnchangedWebSettings( bool? debuggingEnabled; WebSetting userAgent = const WebSetting.absent(); bool? zoomEnabled; + bool? allowsLinkPreview; + if (currentValue.javascriptMode != newValue.javascriptMode) { javascriptMode = newValue.javascriptMode; } @@ -424,6 +436,9 @@ WebSettings _clearUnchangedWebSettings( if (currentValue.zoomEnabled != newValue.zoomEnabled) { zoomEnabled = newValue.zoomEnabled; } + if (currentValue.allowsLinkPreview != newValue.allowsLinkPreview) { + allowsLinkPreview = newValue.allowsLinkPreview; + } return WebSettings( javascriptMode: javascriptMode, @@ -432,6 +447,7 @@ WebSettings _clearUnchangedWebSettings( debuggingEnabled: debuggingEnabled, userAgent: userAgent, zoomEnabled: zoomEnabled, + allowsLinkPreview: allowsLinkPreview, ); } diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index a02b0323e7ab..ae9271d83d4f 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/plugins/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: 3.0.4 +version: 3.0.5 environment: sdk: ">=2.14.0 <3.0.0" @@ -23,6 +23,15 @@ dependencies: webview_flutter_platform_interface: ^1.9.3 webview_flutter_wkwebview: ^2.7.0 +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_android: + path: ../webview_flutter_android + webview_flutter_platform_interface: + path: ../webview_flutter_platform_interface + webview_flutter_wkwebview: + path: ../webview_flutter_wkwebview + dev_dependencies: build_runner: ^2.1.5 flutter_driver: 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 b10366c82024..79fb30264451 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/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, ), ))); @@ -1325,6 +1378,7 @@ class MatchesWebSettings extends Matcher { _webSettings!.debuggingEnabled == webSettings.debuggingEnabled && _webSettings!.gestureNavigationEnabled == webSettings.gestureNavigationEnabled && + _webSettings!.allowsLinkPreview == webSettings.allowsLinkPreview && _webSettings!.userAgent == webSettings.userAgent && _webSettings!.zoomEnabled == webSettings.zoomEnabled; } diff --git a/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md b/packages/webview_flutter/webview_flutter_platform_interface/CHANGELOG.md index 17b86523f29a..28239b6f9d59 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.9.4 + +* Adds support for the `allowsLinkPreview` property on iOS. + ## 1.9.3 * Updates minimum Flutter version to 2.10. diff --git a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart index 102ab10ccea7..804711cbd3fe 100644 --- a/packages/webview_flutter/webview_flutter_platform_interface/lib/src/types/web_settings.dart +++ b/packages/webview_flutter/webview_flutter_platform_interface/lib/src/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/pubspec.yaml b/packages/webview_flutter/webview_flutter_platform_interface/pubspec.yaml index 5b98154998a8..4b148725ff8f 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/main/packages/webview_flutte 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.9.3 +version: 1.9.4 environment: sdk: ">=2.12.0 <3.0.0" diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 056d1cb1c26a..cabd466b7b4a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,5 +1,6 @@ ## 2.9.4 +* Adds support for the `allowsLinkPreview` property on iOS. * Fixes avoid_redundant_argument_values lint warnings and minor typos. * Fixes typo in an internal method name, from `setCookieForInsances` to `setCookieForInstances`. 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 a0026ca01f41..d11485b70174 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 @@ -346,6 +346,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/web_view.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/web_view.dart index c44c4e743669..ca4817ad0b98 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 @@ -66,6 +66,7 @@ class WebView extends StatefulWidget { this.onWebResourceError, this.debuggingEnabled = false, this.gestureNavigationEnabled = false, + this.allowsLinkPreview = true, this.userAgent, this.zoomEnabled = true, this.initialMediaPlaybackPolicy = @@ -206,6 +207,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. @@ -622,6 +630,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 1daa96854f90..24b84a516285 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -19,6 +19,11 @@ dependencies: # the parent directory to use the current plugin's version. path: ../ +# FOR TESTING ONLY. DO NOT MERGE. +dependency_overrides: + webview_flutter_platform_interface: + path: ../../webview_flutter_platform_interface + dev_dependencies: espresso: ^0.1.0+2 flutter_driver: 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 8cbd2c7c194c..c19d383a7262 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.h @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v3.1.5), do not edit directly. +// Autogenerated from Pigeon (v3.2.6), do not edit directly. // See also: https://pub.dev/packages/pigeon #import @protocol FlutterBinaryMessenger; @@ -506,6 +506,9 @@ 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; 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 10680227ee43..1d79a3dde55e 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFGeneratedWebKitApis.m @@ -1,7 +1,7 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v3.1.5), do not edit directly. +// Autogenerated from Pigeon (v3.2.6), do not edit directly. // See also: https://pub.dev/packages/pigeon #import "FWFGeneratedWebKitApis.h" #import @@ -2502,6 +2502,31 @@ void FWFWKWebViewHostApiSetup(id binaryMessenger, [channel setMessageHandler:nil]; } } + { + FlutterBasicMessageChannel *channel = [[FlutterBasicMessageChannel alloc] + initWithName:@"dev.flutter.pigeon.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:@"dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent" 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 ceaa346c8747..cc83dae9760f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FWFWebViewHostApi.m @@ -262,6 +262,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 @@ -287,4 +295,5 @@ - (void)setUIDelegateForWebViewWithIdentifier:(nonnull NSNumber *)identifier error:(FlutterError *_Nullable __autoreleasing *_Nonnull)error { return [[self webViewForIdentifier:identifier] title]; } + @end diff --git a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart index 54bb3015af64..c6a1d6d475d7 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/common/web_kit.pigeon.dart @@ -1,10 +1,9 @@ // Copyright 2013 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// Autogenerated from Pigeon (v3.1.5), do not edit directly. +// Autogenerated from Pigeon (v3.2.6), do not edit directly. // See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name -// @dart = 2.12 +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import import 'dart:async'; import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List; @@ -2354,6 +2353,30 @@ class WKWebViewHostApi { } } + Future setAllowsLinkPreview(int arg_identifier, bool arg_allow) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsLinkPreview', codec, + binaryMessenger: _binaryMessenger); + final Map? replyMap = await channel + .send([arg_identifier, arg_allow]) as Map?; + if (replyMap == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyMap['error'] != null) { + final Map error = + (replyMap['error'] as Map?)!; + throw PlatformException( + code: (error['code'] as String?)!, + message: error['message'] as String?, + details: error['details'], + ); + } 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/web_kit/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit/web_kit.dart index 566c46fda8bb..21a68e24513e 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 @@ -1027,6 +1027,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 614d0793e5f9..e2971fc98314 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 @@ -980,6 +980,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/web_kit_webview_widget.dart b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart index 327210983ae2..361caee31589 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/lib/src/web_kit_webview_widget.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/lib/src/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/pigeons/web_kit.dart b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart index c20a10ebfadd..4add33d3f4ee 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/pigeons/web_kit.dart @@ -570,6 +570,9 @@ abstract class WKWebViewHostApi { @ObjCSelector('setAllowsBackForwardForWebViewWithIdentifier:isAllowed:') void setAllowsBackForwardNavigationGestures(int identifier, bool allow); + @ObjCSelector('setAllowsLinkPreviewForWebViewWithIdentifier:isAllowed:') + void setAllowsLinkPreview(int identifier, bool allow); + @ObjCSelector('setUserAgentForWebViewWithIdentifier: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 40b7bf89601d..25fac85b3c8c 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml @@ -21,6 +21,11 @@ dependencies: path: ^1.8.0 webview_flutter_platform_interface: ^1.9.3 +# 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_driver: diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart index a9e5c8bb1db4..a9594656eb0f 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/common/test_web_kit.pigeon.dart @@ -964,6 +964,7 @@ abstract class TestWKWebViewHostApi { void reload(int identifier); String? getTitle(int identifier); void setAllowsBackForwardNavigationGestures(int identifier, bool allow); + void setAllowsLinkPreview(int identifier, bool allow); void setCustomUserAgent(int identifier, String? userAgent); Future evaluateJavaScript(int identifier, String javaScriptString); static void setup(TestWKWebViewHostApi? api, @@ -1299,6 +1300,28 @@ abstract class TestWKWebViewHostApi { }); } } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.WKWebViewHostApi.setAllowsLinkPreview', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.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.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.WKWebViewHostApi.setAllowsLinkPreview was null, expected non-null bool.'); + api.setAllowsLinkPreview(arg_identifier!, arg_allow!); + return {}; + }); + } + } { final BasicMessageChannel channel = BasicMessageChannel( 'dev.flutter.pigeon.WKWebViewHostApi.setCustomUserAgent', codec, 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 a382ecff677c..7bd65080b012 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 @@ -135,6 +135,10 @@ class MockTestWKWebViewHostApi extends _i1.Mock #setAllowsBackForwardNavigationGestures, [identifier, allow]), returnValueForMissingStub: null); @override + void setAllowsLinkPreview(int? identifier, bool? allow) => super.noSuchMethod( + Invocation.method(#setAllowsLinkPreview, [identifier, allow]), + returnValueForMissingStub: null); + @override void setCustomUserAgent(int? identifier, String? userAgent) => super.noSuchMethod( Invocation.method(#setCustomUserAgent, [identifier, userAgent]), 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 4000e0d718da..841ede6abb4d 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 @@ -816,6 +816,13 @@ void main() { false, )); }); + test('setAllowsLinkPreview', () { + webView.setAllowsLinkPreview(false); + verify(mockPlatformHostApi.setAllowsLinkPreview( + webViewInstanceId, + false, + )); + }); test('customUserAgent', () { webView.setCustomUserAgent('hello'); 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 18f30d434952..c431aef1f246 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 @@ -268,6 +268,10 @@ class MockTestWKWebViewHostApi extends _i1.Mock #setAllowsBackForwardNavigationGestures, [identifier, allow]), returnValueForMissingStub: null); @override + void setAllowsLinkPreview(int? identifier, bool? allow) => super.noSuchMethod( + Invocation.method(#setAllowsLinkPreview, [identifier, allow]), + returnValueForMissingStub: null); + @override void setCustomUserAgent(int? identifier, String? userAgent) => super.noSuchMethod( Invocation.method(#setCustomUserAgent, [identifier, userAgent]), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart index f216711ca0b2..ee3acf7b074a 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/src/web_kit_webview_widget_test.mocks.dart @@ -305,6 +305,11 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future setAllowsLinkPreview(bool? allow) => + (super.noSuchMethod(Invocation.method(#setAllowsLinkPreview, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), returnValue: Future.value(), diff --git a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart index 17d47917b2ee..b0bbe15e938b 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/test/v4/webkit_webview_controller_test.mocks.dart @@ -319,6 +319,11 @@ class MockWKWebView extends _i1.Mock implements _i4.WKWebView { returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i5.Future); @override + _i5.Future setAllowsLinkPreview(bool? allow) => + (super.noSuchMethod(Invocation.method(#setAllowsLinkPreview, [allow]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i5.Future); + @override _i5.Future setCustomUserAgent(String? userAgent) => (super.noSuchMethod(Invocation.method(#setCustomUserAgent, [userAgent]), returnValue: Future.value(),