Skip to content

[webview_flutter] Adds onHttpError callback to NavigationDelegate to catch HTTP error status codes #6378

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 5 commits into from
May 24, 2024
Merged
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.8.0

* Adds `onHttpError` callback to `NavigationDelegate` to catch HTTP error status codes.

## 4.7.0

* Adds support to track scroll position changes.
Expand Down
1 change: 1 addition & 0 deletions packages/webview_flutter/webview_flutter/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ controller = WebViewController()
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onHttpError: (HttpResponseError error) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,65 @@ Future<void> main() async {
expect(currentUrl, isNot(contains('youtube.com')));
});

testWidgets('onHttpError', (WidgetTester tester) async {
final Completer<HttpResponseError> errorCompleter =
Completer<HttpResponseError>();

final WebViewController controller = WebViewController();
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));

final NavigationDelegate delegate = NavigationDelegate(
onHttpError: (HttpResponseError error) {
errorCompleter.complete(error);
},
);
unawaited(controller.setNavigationDelegate(delegate));

unawaited(controller.loadRequest(
Uri.parse('$prefixUrl/favicon.ico'),
));

await tester.pumpWidget(WebViewWidget(controller: controller));

final HttpResponseError error = await errorCompleter.future;

expect(error, isNotNull);
expect(error.response?.statusCode, 404);
});

testWidgets('onHttpError is not called when no HTTP error is received',
(WidgetTester tester) async {
const String testPage = '''
<!DOCTYPE html><html>
</head>
<body>
</body>
</html>
''';

final Completer<HttpResponseError> errorCompleter =
Completer<HttpResponseError>();
final Completer<void> pageFinishCompleter = Completer<void>();

final WebViewController controller = WebViewController();
unawaited(controller.setJavaScriptMode(JavaScriptMode.unrestricted));

final NavigationDelegate delegate = NavigationDelegate(
onPageFinished: pageFinishCompleter.complete,
onHttpError: (HttpResponseError error) {
errorCompleter.complete(error);
},
);
unawaited(controller.setNavigationDelegate(delegate));

unawaited(controller.loadHtmlString(testPage));

await tester.pumpWidget(WebViewWidget(controller: controller));

expect(errorCompleter.future, doesNotComplete);
await pageFinishCompleter.future;
});

testWidgets('supports asynchronous decisions', (WidgetTester tester) async {
Completer<void> pageLoaded = Completer<void>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ Page resource error:
debugPrint('allowing navigation to ${request.url}');
return NavigationDecision.navigate;
},
onHttpError: (HttpResponseError error) {
debugPrint('Error occurred on page: ${error.response?.statusCode}');
},
onUrlChange: (UrlChange change) {
debugPrint('url change to ${change.url}');
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class _WebViewExampleState extends State<WebViewExample> {
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onHttpError: (HttpResponseError error) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ dependencies:
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
webview_flutter_android: ^3.15.0
webview_flutter_wkwebview: ^3.12.0
webview_flutter_android: ^3.16.0
webview_flutter_wkwebview: ^3.13.0

dev_dependencies:
build_runner: ^2.1.5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,4 +121,7 @@ class FakeNavigationDelegate extends PlatformNavigationDelegate {
Future<void> setOnHttpAuthRequest(
HttpAuthRequestCallback handler,
) async {}

@override
Future<void> setOnHttpError(HttpResponseErrorCallback onHttpError) async {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ class NavigationDelegate {
void Function(WebResourceError error)? onWebResourceError,
void Function(UrlChange change)? onUrlChange,
void Function(HttpAuthRequest request)? onHttpAuthRequest,
void Function(HttpResponseError error)? onHttpError,
}) : this.fromPlatformCreationParams(
const PlatformNavigationDelegateCreationParams(),
onNavigationRequest: onNavigationRequest,
Expand All @@ -59,6 +60,7 @@ class NavigationDelegate {
onWebResourceError: onWebResourceError,
onUrlChange: onUrlChange,
onHttpAuthRequest: onHttpAuthRequest,
onHttpError: onHttpError,
);

/// Constructs a [NavigationDelegate] from creation params for a specific
Expand Down Expand Up @@ -102,6 +104,7 @@ class NavigationDelegate {
void Function(WebResourceError error)? onWebResourceError,
void Function(UrlChange change)? onUrlChange,
void Function(HttpAuthRequest request)? onHttpAuthRequest,
void Function(HttpResponseError error)? onHttpError,
}) : this.fromPlatform(
PlatformNavigationDelegate(params),
onNavigationRequest: onNavigationRequest,
Expand All @@ -111,6 +114,7 @@ class NavigationDelegate {
onWebResourceError: onWebResourceError,
onUrlChange: onUrlChange,
onHttpAuthRequest: onHttpAuthRequest,
onHttpError: onHttpError,
);

/// Constructs a [NavigationDelegate] from a specific platform implementation.
Expand All @@ -125,6 +129,7 @@ class NavigationDelegate {
this.onWebResourceError,
void Function(UrlChange change)? onUrlChange,
HttpAuthRequestCallback? onHttpAuthRequest,
void Function(HttpResponseError error)? onHttpError,
}) {
if (onNavigationRequest != null) {
platform.setOnNavigationRequest(onNavigationRequest!);
Expand All @@ -147,6 +152,9 @@ class NavigationDelegate {
if (onHttpAuthRequest != null) {
platform.setOnHttpAuthRequest(onHttpAuthRequest);
}
if (onHttpError != null) {
platform.setOnHttpError(onHttpError);
}
}

/// Implementation of [PlatformNavigationDelegate] for the current platform.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
export 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'
show
HttpAuthRequest,
HttpResponseError,
HttpResponseErrorCallback,
JavaScriptAlertDialogRequest,
JavaScriptConfirmDialogRequest,
JavaScriptConsoleMessage,
Expand All @@ -28,6 +30,8 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte
WebResourceError,
WebResourceErrorCallback,
WebResourceErrorType,
WebResourceRequest,
WebResourceResponse,
WebViewCookie,
WebViewCredential,
WebViewPermissionResourceType,
Expand Down
6 changes: 3 additions & 3 deletions 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.7.0
version: 4.8.0

environment:
sdk: ^3.2.3
Expand All @@ -19,9 +19,9 @@ flutter:
dependencies:
flutter:
sdk: flutter
webview_flutter_android: ^3.15.0
webview_flutter_android: ^3.16.0
webview_flutter_platform_interface: ^2.10.0
webview_flutter_wkwebview: ^3.12.0
webview_flutter_wkwebview: ^3.13.0

dev_dependencies:
build_runner: ^2.1.5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,18 @@ void main() {

verify(delegate.platform.setOnHttpAuthRequest(onHttpAuthRequest));
});

test('onHttpError', () async {
WebViewPlatform.instance = TestWebViewPlatform();

void onHttpError(HttpResponseError error) {}

final NavigationDelegate delegate = NavigationDelegate(
onHttpError: onHttpError,
);

verify(delegate.platform.setOnHttpError(onHttpError));
});
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ void main() {
'ensure webview_flutter.dart exports classes from platform interface',
() {
main_file.HttpAuthRequest;
main_file.HttpResponseError;
main_file.HttpResponseErrorCallback;
main_file.JavaScriptConsoleMessage;
main_file.JavaScriptLogLevel;
main_file.JavaScriptMessage;
Expand All @@ -34,6 +36,8 @@ void main() {
main_file.WebViewCookie;
main_file.WebViewCredential;
main_file.WebResourceErrorType;
main_file.WebResourceRequest;
main_file.WebResourceResponse;
main_file.UrlChange;
},
);
Expand Down