From 3e3d799309bf69cca02ec70747921bbef42e9dba Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 6 Dec 2021 15:50:08 +0100 Subject: [PATCH 1/3] Update example to demonstrate use of `loadFlutterAsset` --- .../example/assets/www/index.html | 17 +++++++++++++++++ .../example/assets/www/styles/style.css | 3 +++ .../example/lib/main.dart | 13 +++++++++++++ .../example/lib/web_view.dart | 8 ++++++++ .../example/pubspec.yaml | 2 ++ 5 files changed, 43 insertions(+) create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html create mode 100644 packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html new file mode 100644 index 000000000000..51bc536cccdc --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html @@ -0,0 +1,17 @@ + + + +Load file or HTML string example + + + + +

Local demo page

+

+ This is an example page used to demonstrate how to load a local file or HTML + string using the Flutter + webview plugin. +

+ + + \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css new file mode 100644 index 000000000000..c2140b8b0fd8 --- /dev/null +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/styles/style.css @@ -0,0 +1,3 @@ +h1 { + color: blue; +} \ No newline at end of file diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart index 41297bfd94fe..8f70300250f5 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/lib/main.dart @@ -166,6 +166,7 @@ enum _MenuOptions { listCache, clearCache, navigationDelegate, + loadFlutterAsset, loadLocalFile, loadHtmlString, doPostRequest, @@ -208,6 +209,9 @@ class _SampleMenu extends StatelessWidget { case _MenuOptions.navigationDelegate: _onNavigationDelegateExample(controller.data!, context); break; + case _MenuOptions.loadFlutterAsset: + _onLoadFlutterAssetExample(controller.data!, context); + break; case _MenuOptions.loadLocalFile: _onLoadLocalFileExample(controller.data!, context); break; @@ -252,6 +256,10 @@ class _SampleMenu extends StatelessWidget { value: _MenuOptions.navigationDelegate, child: Text('Navigation Delegate example'), ), + const PopupMenuItem<_MenuOptions>( + value: _MenuOptions.loadFlutterAsset, + child: Text('Load Flutter Asset'), + ), const PopupMenuItem<_MenuOptions>( value: _MenuOptions.loadHtmlString, child: Text('Load HTML string'), @@ -342,6 +350,11 @@ class _SampleMenu extends StatelessWidget { await controller.loadUrl('data:text/html;base64,$contentBase64'); } + Future _onLoadFlutterAssetExample( + WebViewController controller, BuildContext context) async { + await controller.loadFlutterAsset('assets/www/index.html'); + } + Future _onLoadLocalFileExample( WebViewController controller, BuildContext context) async { final String pathToIndex = await _prepareLocalFile(); 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 69fa2bdb79e3..ed953545cac1 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 @@ -313,6 +313,14 @@ class WebViewController { WebView _widget; + /// Loads the Flutter asset specified in the pubspec.yaml file. + /// + /// Throws an ArgumentError if [key] is not part of the specified assets + /// in the pubspec.yaml file. + Future loadFlutterAsset(String key) { + return _webViewPlatformController.loadFlutterAsset(key); + } + /// Loads the file located on the specified [absoluteFilePath]. /// /// The [absoluteFilePath] parameter should contain the absolute path to the diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml index 2070b1161ba0..b8c2464eb051 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/pubspec.yaml @@ -34,3 +34,5 @@ flutter: assets: - assets/sample_audio.ogg - assets/sample_video.mp4 + - assets/www/index.html + - assets/www/styles/style.css From 9b5ee4a23c876c921a9e2f5466f8a70eabee683f Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Mon, 6 Dec 2021 18:01:35 +0100 Subject: [PATCH 2/3] Implements `loadFlutterAsset` method from platform interface. --- .../webview_flutter_wkwebview/CHANGELOG.md | 4 + .../example/assets/www/index.html | 3 + .../example/ios/RunnerTests/FLTWebViewTests.m | 117 +++++++++++++++++- .../ios/Classes/FlutterWebView.m | 30 +++++ .../webview_flutter_wkwebview/pubspec.yaml | 2 +- 5 files changed, 153 insertions(+), 3 deletions(-) diff --git a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md index 22f45f27c4d7..b7fb720bfee4 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md +++ b/packages/webview_flutter/webview_flutter_wkwebview/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.6.0 + +* Adds implementation of the `loadFlutterAsset` method from the platform interface. + ## 2.5.0 * Adds an option to set the background color of the webview. diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html index 51bc536cccdc..9895dd3ce6cb 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/assets/www/index.html @@ -1,4 +1,7 @@ + Load file or HTML string example diff --git a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m index a3c314a79d2a..264cf09d32f6 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/example/ios/RunnerTests/FLTWebViewTests.m @@ -161,7 +161,120 @@ - (void)testLoadFileFailsWithInvalidPath { OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); } -- (void)testLoadFileSucceedsWithBaseUrl { +- (void)testLoadFlutterAssetSucceeds { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; + NSURL *url = [NSURL URLWithString:[@"file:///" stringByAppendingString:filePath]]; + [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] + withExtension:@"html"]) andReturn:(url)]; + + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Should return successful result over the method channel."]; + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@"assets/file.html"] + result:^(id _Nullable result) { + XCTAssertNil(result); + [resultExpectation fulfill]; + }]; + + [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; + OCMVerify([mockWebView loadFileURL:url + allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]); +} + +- (void)testLoadFlutterAssetFailsWithInvalidKey { + NSArray *resultExpectations = @[ + [self expectationWithDescription:@"Should return failed result when argument is nil."], + [self expectationWithDescription: + @"Should return failed result when argument is not of type NSString*."], + [self expectationWithDescription: + @"Should return failed result when argument is an empty string."], + ]; + + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:nil] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument is nil."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[0] fulfill]; + }]; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@(10)] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument is not of type NSString."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[1] fulfill]; + }]; + [controller onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@""] + result:^(id _Nullable result) { + FlutterError *expected = + [FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:@"Argument contains an empty string."]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectations[2] fulfill]; + }]; + + [self waitForExpectations:resultExpectations timeout:1.0]; + OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); +} + +- (void)testLoadFlutterAssetFailsWithParsingError { + NSBundle *mockBundle = OCMPartialMock([NSBundle mainBundle]); + NSString *filePath = [FlutterDartProject lookupKeyForAsset:@"assets/file.html"]; + [OCMStub([mockBundle URLForResource:[filePath stringByDeletingPathExtension] + withExtension:@"html"]) andReturn:(nil)]; + + XCTestExpectation *resultExpectation = + [self expectationWithDescription:@"Should return failed result over the method channel."]; + FLTWebViewController *controller = + [[FLTWebViewController alloc] initWithFrame:CGRectMake(0, 0, 300, 400) + viewIdentifier:1 + arguments:nil + binaryMessenger:self.mockBinaryMessenger]; + FLTWKWebView *mockWebView = OCMClassMock(FLTWKWebView.class); + controller.webView = mockWebView; + [controller + onMethodCall:[FlutterMethodCall methodCallWithMethodName:@"loadFlutterAsset" + arguments:@"assets/file.html"] + result:^(id _Nullable result) { + FlutterError *expected = [FlutterError + errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Failed parsing file path for supplied key." + details:[NSString + stringWithFormat: + @"Failed to convert path '%@' into NSURL for key '%@'.", + filePath, @"assets/file.html"]]; + [FLTWebViewTests assertFlutterError:result withExpected:expected]; + [resultExpectation fulfill]; + }]; + + [self waitForExpectations:@[ resultExpectation ] timeout:1.0]; + OCMReject([mockWebView loadFileURL:[OCMArg any] allowingReadAccessToURL:[OCMArg any]]); +} + +- (void)testLoadHtmlStringSucceedsWithBaseUrl { NSURL *baseUrl = [NSURL URLWithString:@"https://flutter.dev"]; XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return successful result over the method channel."]; @@ -186,7 +299,7 @@ - (void)testLoadFileSucceedsWithBaseUrl { OCMVerify([mockWebView loadHTMLString:@"some HTML string" baseURL:baseUrl]); } -- (void)testLoadFileSucceedsWithoutBaseUrl { +- (void)testLoadHtmlStringSucceedsWithoutBaseUrl { XCTestExpectation *resultExpectation = [self expectationWithDescription:@"Should return successful result over the method channel."]; FLTWebViewController *controller = diff --git a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m index 25a93a67addc..2cbab71ec247 100644 --- a/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/webview_flutter_wkwebview/ios/Classes/FlutterWebView.m @@ -156,6 +156,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { [self onUpdateSettings:call result:result]; } else if ([[call method] isEqualToString:@"loadFile"]) { [self onLoadFile:call result:result]; + } else if ([[call method] isEqualToString:@"loadFlutterAsset"]) { + [self onLoadFlutterAsset:call result:result]; } else if ([[call method] isEqualToString:@"loadHtmlString"]) { [self onLoadHtmlString:call result:result]; } else if ([[call method] isEqualToString:@"loadUrl"]) { @@ -237,6 +239,34 @@ - (void)onLoadFile:(FlutterMethodCall*)call result:(FlutterResult)result { result(nil); } +- (void)onLoadFlutterAsset:(FlutterMethodCall*)call result:(FlutterResult)result { + NSString* error = nil; + if (![FLTWebViewController isValidStringArgument:[call arguments] withErrorMessage:&error]) { + result([FlutterError errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Supplied asset key is not valid." + details:error]); + return; + } + + NSString* assetKey = [call arguments]; + NSString* assetFilePath = [FlutterDartProject lookupKeyForAsset:assetKey]; + NSURL* url = [[NSBundle mainBundle] URLForResource:[assetFilePath stringByDeletingPathExtension] + withExtension:assetFilePath.pathExtension]; + + if (!url) { + result([FlutterError + errorWithCode:@"loadFlutterAsset_invalidKey" + message:@"Failed parsing file path for supplied key." + details:[NSString + stringWithFormat:@"Failed to convert path '%@' into NSURL for key '%@'.", + assetFilePath, assetKey]]); + return; + } + + [_webView loadFileURL:url allowingReadAccessToURL:[url URLByDeletingLastPathComponent]]; + result(nil); +} + - (void)onLoadHtmlString:(FlutterMethodCall*)call result:(FlutterResult)result { NSDictionary* arguments = [call arguments]; if (![arguments isKindOfClass:NSDictionary.class]) { diff --git a/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml b/packages/webview_flutter/webview_flutter_wkwebview/pubspec.yaml index 702473382793..29da7ca355bd 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/plugins/tree/master/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: 2.5.0 +version: 2.6.0 environment: sdk: ">=2.14.0 <3.0.0" From 564d091c2c7187086463f3f308df145dfe9d4d54 Mon Sep 17 00:00:00 2001 From: Maurits van Beusekom Date: Tue, 7 Dec 2021 10:30:23 +0100 Subject: [PATCH 3/3] Trigger build