From 69d9cf3546fd763a9c7bc1a06ac5fa863b0d7441 Mon Sep 17 00:00:00 2001 From: BeMacized Date: Tue, 7 Dec 2021 19:00:07 +0100 Subject: [PATCH] [webview_flutter] Add loadRequest functionality to app facing package. (#4573) * Add android implementations for loadRequest. * Update changelog and pubspec. * Fix comment. * Fix comment. * Add tests * Add back removed license headers * Fix analysis error * Add support for loadRequest to app facing package * Add test * Comment pending dependency in pubspec * Remove workaround for supporting custom headers when making post requests on Android. * Document android workaround in dart doc * Enforce uri scheme * Update loadRequest dartdoc * Document android workaround in readme * Processed PR feedback. * Updated dependency, version and changelog. --- .../webview_flutter/CHANGELOG.md | 4 ++ .../webview_flutter/webview_flutter/README.md | 5 +++ .../webview_flutter/example/lib/main.dart | 20 ++++++++++ .../lib/platform_interface.dart | 4 +- .../webview_flutter/lib/src/webview.dart | 20 ++++++++++ .../webview_flutter/pubspec.yaml | 6 +-- .../test/webview_flutter_test.dart | 25 +++++++++++++ .../test/webview_flutter_test.mocks.dart | 37 ++++++++++++------- 8 files changed, 103 insertions(+), 18 deletions(-) diff --git a/packages/webview_flutter/webview_flutter/CHANGELOG.md b/packages/webview_flutter/webview_flutter/CHANGELOG.md index 1f8a33db4cb4..a9e133d08d47 100644 --- a/packages/webview_flutter/webview_flutter/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.0 + +* Adds support for the `loadRequest` method. + ## 2.5.0 * Adds an option to set the background color of the webview. diff --git a/packages/webview_flutter/webview_flutter/README.md b/packages/webview_flutter/webview_flutter/README.md index de475ad9acd7..c3982eab2bad 100644 --- a/packages/webview_flutter/webview_flutter/README.md +++ b/packages/webview_flutter/webview_flutter/README.md @@ -92,3 +92,8 @@ android { To use Material Components when the user interacts with input elements in the WebView, follow the steps described in the [Enabling Material Components instructions](https://flutter.dev/docs/deployment/android#enabling-material-components). + +### Setting custom headers on POST requests + +Currently, setting custom headers when making a post request with the WebViewController's `loadRequest` method is not supported on Android. +If you require this functionality, a workaround is to make the request manually, and then load the response data using `loadHTMLString` instead. \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter/example/lib/main.dart b/packages/webview_flutter/webview_flutter/example/lib/main.dart index c870ae9d829d..02e6ef85ec61 100644 --- a/packages/webview_flutter/webview_flutter/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter/example/lib/main.dart @@ -7,6 +7,7 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:path_provider/path_provider.dart'; @@ -176,6 +177,7 @@ enum MenuOptions { listCache, clearCache, navigationDelegate, + doPostRequest, loadLocalFile, loadHtmlString, transparentBackground, @@ -218,6 +220,9 @@ class SampleMenu extends StatelessWidget { case MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case MenuOptions.doPostRequest: + _onDoPostRequest(controller.data!, context); + break; case MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; @@ -259,6 +264,10 @@ class SampleMenu extends StatelessWidget { value: MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem( + value: MenuOptions.doPostRequest, + child: Text('Post Request'), + ), const PopupMenuItem( value: MenuOptions.loadHtmlString, child: Text('Load HTML string'), @@ -348,6 +357,17 @@ class SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onDoPostRequest( + WebViewController controller, BuildContext context) async { + final WebViewRequest request = WebViewRequest( + uri: Uri.parse('https://httpbin.org/post'), + method: WebViewRequestMethod.post, + headers: {'foo': 'bar', 'Content-Type': 'text/plain'}, + body: Uint8List.fromList('Test Body'.codeUnits), + ); + await controller.loadRequest(request); + } + Future _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { final String pathToIndex = await _prepareLocalFile(); diff --git a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart index aa7b3a0931e8..ab1cbb1bd344 100644 --- a/packages/webview_flutter/webview_flutter/lib/platform_interface.dart +++ b/packages/webview_flutter/webview_flutter/lib/platform_interface.dart @@ -21,4 +21,6 @@ export 'package:webview_flutter_platform_interface/webview_flutter_platform_inte WebSetting, WebSettings, WebResourceError, - WebResourceErrorType; + WebResourceErrorType, + WebViewRequest, + WebViewRequestMethod; diff --git a/packages/webview_flutter/webview_flutter/lib/src/webview.dart b/packages/webview_flutter/webview_flutter/lib/src/webview.dart index b2cc69045e5f..d76f7b359482 100644 --- a/packages/webview_flutter/webview_flutter/lib/src/webview.dart +++ b/packages/webview_flutter/webview_flutter/lib/src/webview.dart @@ -550,6 +550,26 @@ class WebViewController { return _webViewPlatformController.loadUrl(url, headers); } + /// Makes a specific HTTP request and loads the response in the webview. + /// + /// [WebViewRequest.method] must be one of the supported HTTP methods + /// in [WebViewRequestMethod]. + /// + /// If [WebViewRequest.headers] is not empty, its key-value pairs will be + /// added as the headers for the request. + /// + /// If [WebViewRequest.body] is not null, it will be added as the body + /// for the request. + /// + /// Throws an ArgumentError if [WebViewRequest.uri] has empty scheme. + /// + /// Android only: + /// When making a POST request, headers are ignored. As a workaround, make + /// the request manually and load the response data using [loadHTMLString]. + Future loadRequest(WebViewRequest request) async { + return _webViewPlatformController.loadRequest(request); + } + /// Accessor to the current URL that the WebView is displaying. /// /// If [WebView.initialUrl] was never specified, returns `null`. diff --git a/packages/webview_flutter/webview_flutter/pubspec.yaml b/packages/webview_flutter/webview_flutter/pubspec.yaml index 6c00f65d8022..1bc6bab8b3ff 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/master/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: 2.5.0 +version: 2.6.0 environment: sdk: ">=2.14.0 <3.0.0" @@ -19,8 +19,8 @@ flutter: dependencies: flutter: sdk: flutter - webview_flutter_android: ^2.5.0 - webview_flutter_platform_interface: ^1.7.0 + webview_flutter_android: ^2.7.0 + webview_flutter_platform_interface: ^1.8.0 webview_flutter_wkwebview: ^2.5.0 dev_dependencies: 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 da860f0118d4..d422402dbae7 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:typed_data'; + import 'package:flutter/src/foundation/basic_types.dart'; import 'package:flutter/src/gestures/recognizer.dart'; import 'package:flutter/widgets.dart'; @@ -246,6 +248,29 @@ void main() { )); }); + testWidgets('loadRequest', (WidgetTester tester) async { + WebViewController? controller; + await tester.pumpWidget( + WebView( + onWebViewCreated: (WebViewController webViewController) { + controller = webViewController; + }, + ), + ); + expect(controller, isNotNull); + + final WebViewRequest req = WebViewRequest( + uri: Uri.parse('https://flutter.dev'), + method: WebViewRequestMethod.post, + headers: {'foo': 'bar'}, + body: Uint8List.fromList('Test Body'.codeUnits), + ); + + await controller!.loadRequest(req); + + verify(mockWebViewPlatformController.loadRequest(req)); + }); + testWidgets('Clear Cache', (WidgetTester tester) async { WebViewController? controller; await tester.pumpWidget( diff --git a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart index 8857f606f06d..fe3060164df2 100644 --- a/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart +++ b/packages/webview_flutter/webview_flutter/test/webview_flutter_test.mocks.dart @@ -1,22 +1,26 @@ +// 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. + // Mocks generated by Mockito 5.0.16 from annotations // in webview_flutter/test/webview_flutter_test.dart. // Do not manually edit this file. import 'dart:async' as _i9; -import 'package:flutter/foundation.dart' as _i7; +import 'package:flutter/foundation.dart' as _i3; import 'package:flutter/gestures.dart' as _i8; import 'package:flutter/widgets.dart' as _i2; import 'package:mockito/mockito.dart' as _i1; import 'package:webview_flutter_platform_interface/src/platform_interface/javascript_channel_registry.dart' - as _i6; + as _i7; import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform.dart' - as _i3; + as _i4; import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform_callbacks_handler.dart' - as _i5; + as _i6; import 'package:webview_flutter_platform_interface/src/platform_interface/webview_platform_controller.dart' as _i10; -import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i4; +import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i5; // ignore_for_file: avoid_redundant_argument_values // ignore_for_file: avoid_setters_without_getters @@ -29,14 +33,14 @@ import 'package:webview_flutter_platform_interface/src/types/types.dart' as _i4; class _FakeWidget_0 extends _i1.Fake implements _i2.Widget { @override - String toString({_i2.DiagnosticLevel? minLevel = _i2.DiagnosticLevel.info}) => + String toString({_i3.DiagnosticLevel? minLevel = _i3.DiagnosticLevel.info}) => super.toString(); } /// A class which mocks [WebViewPlatform]. /// /// See the documentation for Mockito's code generation for more information. -class MockWebViewPlatform extends _i1.Mock implements _i3.WebViewPlatform { +class MockWebViewPlatform extends _i1.Mock implements _i4.WebViewPlatform { MockWebViewPlatform() { _i1.throwOnMissingStub(this); } @@ -44,11 +48,11 @@ class MockWebViewPlatform extends _i1.Mock implements _i3.WebViewPlatform { @override _i2.Widget build( {_i2.BuildContext? context, - _i4.CreationParams? creationParams, - _i5.WebViewPlatformCallbacksHandler? webViewPlatformCallbacksHandler, - _i6.JavascriptChannelRegistry? javascriptChannelRegistry, - _i3.WebViewPlatformCreatedCallback? onWebViewPlatformCreated, - Set<_i7.Factory<_i8.OneSequenceGestureRecognizer>>? + _i5.CreationParams? creationParams, + _i6.WebViewPlatformCallbacksHandler? webViewPlatformCallbacksHandler, + _i7.JavascriptChannelRegistry? javascriptChannelRegistry, + _i4.WebViewPlatformCreatedCallback? onWebViewPlatformCreated, + Set<_i3.Factory<_i8.OneSequenceGestureRecognizer>>? gestureRecognizers}) => (super.noSuchMethod( Invocation.method(#build, [], { @@ -83,6 +87,11 @@ class MockWebViewPlatformController extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override + _i9.Future loadFlutterAsset(String? key) => + (super.noSuchMethod(Invocation.method(#loadFlutterAsset, [key]), + returnValue: Future.value(), + returnValueForMissingStub: Future.value()) as _i9.Future); + @override _i9.Future loadHtmlString(String? html, {String? baseUrl}) => (super.noSuchMethod( Invocation.method(#loadHtmlString, [html], {#baseUrl: baseUrl}), @@ -94,12 +103,12 @@ class MockWebViewPlatformController extends _i1.Mock returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override - _i9.Future loadRequest(_i4.WebViewRequest? request) => + _i9.Future loadRequest(_i5.WebViewRequest? request) => (super.noSuchMethod(Invocation.method(#loadRequest, [request]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future); @override - _i9.Future updateSettings(_i4.WebSettings? setting) => + _i9.Future updateSettings(_i5.WebSettings? setting) => (super.noSuchMethod(Invocation.method(#updateSettings, [setting]), returnValue: Future.value(), returnValueForMissingStub: Future.value()) as _i9.Future);