Skip to content

[web_view]Expose the allowsLinkPreview property in WKWebView for iOS #5293

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

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/webview_flutter/webview_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 4.4.2

* Adds support for the `allowsLinkPreview` property on iOS.

## 4.4.1

* Exposes `JavaScriptLogLevel` from platform interface.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<String?>.of(widget.userAgent),
zoomEnabled: widget.zoomEnabled,
Expand All @@ -397,13 +406,15 @@ WebSettings _clearUnchangedWebSettings(
assert(newValue.hasNavigationDelegate != null);
assert(newValue.debuggingEnabled != null);
assert(newValue.zoomEnabled != null);
assert(newValue.allowsLinkPreview != null);

JavascriptMode? javascriptMode;
bool? hasNavigationDelegate;
bool? hasProgressTracking;
bool? debuggingEnabled;
WebSetting<String?> userAgent = const WebSetting<String?>.absent();
bool? zoomEnabled;
bool? allowsLinkPreview;
if (currentValue.javascriptMode != newValue.javascriptMode) {
javascriptMode = newValue.javascriptMode;
}
Expand All @@ -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,
Expand All @@ -430,6 +444,7 @@ WebSettings _clearUnchangedWebSettings(
debuggingEnabled: debuggingEnabled,
userAgent: userAgent,
zoomEnabled: zoomEnabled,
allowsLinkPreview: allowsLinkPreview,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,11 @@ class WebViewController {
return platform.setUserAgent(userAgent);
}

/// Whether to display a preview of the destination for the link
Future<void> setAllowsLinkPreview(bool allow) {
return platform.setAllowsLinkPreview(allow);
}

/// Sets a callback that notifies the host application on any log messages
/// written to the JavaScript console.
///
Expand Down
7 changes: 6 additions & 1 deletion packages/webview_flutter/webview_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -1143,6 +1195,7 @@ void main() {
debuggingEnabled: false,
userAgent: const WebSetting<String?>.of(null),
gestureNavigationEnabled: true,
allowsLinkPreview: true,
zoomEnabled: true,
),
)));
Expand Down Expand Up @@ -1326,6 +1379,7 @@ class MatchesWebSettings extends Matcher {
_webSettings!.gestureNavigationEnabled ==
webSettings.gestureNavigationEnabled &&
_webSettings!.userAgent == webSettings.userAgent &&
_webSettings!.allowsLinkPreview == webSettings.allowsLinkPreview &&
_webSettings!.zoomEnabled == webSettings.zoomEnabled;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,17 @@ class MockPlatformWebViewController extends _i1.Mock
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);

@override
_i5.Future<void> setAllowsLinkPreview(bool? allow) => (super.noSuchMethod(
Invocation.method(
#setAllowsLinkPreview,
[allow],
),
returnValue: _i5.Future<void>.value(),
returnValueForMissingStub: _i5.Future<void>.value(),
) as _i5.Future<void>);

@override
_i5.Future<void> setOnPlatformPermissionRequest(
void Function(_i2.PlatformWebViewPermissionRequest)?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,17 @@ class MockPlatformWebViewController extends _i1.Mock
returnValue: _i7.Future<void>.value(),
returnValueForMissingStub: _i7.Future<void>.value(),
) as _i7.Future<void>);

@override
_i7.Future<void> setAllowsLinkPreview(bool? allow) => (super.noSuchMethod(
Invocation.method(
#setAllowsLinkPreview,
[allow],
),
returnValue: _i7.Future<void>.value(),
returnValueForMissingStub: _i7.Future<void>.value(),
) as _i7.Future<void>);

@override
_i7.Future<void> setOnPlatformPermissionRequest(
void Function(_i2.PlatformWebViewPermissionRequest)?
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ class WebSettings {
this.hasProgressTracking,
this.debuggingEnabled,
this.gestureNavigationEnabled,
this.allowsLinkPreview,
this.allowsInlineMediaPlayback,
this.zoomEnabled,
required this.userAgent,
Expand Down Expand Up @@ -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)';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> setAllowsLinkPreview(bool allow) async {}

/// Sets a callback that notifies the host application that web content is
/// requesting permission to access the specified resources.
Future<void> setOnPlatformPermissionRequest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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<String?>.of(widget.userAgent),
zoomEnabled: widget.zoomEnabled,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,12 @@ NSObject<FlutterMessageCodec> *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;
Expand Down
Loading