Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[webview_flutter] Add loadAssetFile to load html file from local assets #1247

Closed
wants to merge 4 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
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ public class FlutterWebView implements PlatformView, MethodCallHandler {

if (params.containsKey("initialUrl")) {
String url = (String) params.get("initialUrl");
webView.loadUrl(url);
if (url.contains("://")) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes it so we accept something that isn't a URL as the initialUrl.
We could consider adding an initialAsset parameter, but I'd leave it for a different PR as it warrants some more discussion (generally it seems like users tend to get confused by the initialFoo parameters)

webView.loadUrl(url);
} else {
webView.loadUrl("file:///android_asset/flutter_assets/" + url);
}
}
}

Expand All @@ -63,6 +67,9 @@ public void onMethodCall(MethodCall methodCall, Result result) {
case "loadUrl":
loadUrl(methodCall, result);
break;
case "loadAssetFile":
loadAssetFile(methodCall, result);
break;
case "updateSettings":
updateSettings(methodCall, result);
break;
Expand Down Expand Up @@ -113,6 +120,12 @@ private void loadUrl(MethodCall methodCall, Result result) {
result.success(null);
}

private void loadAssetFile(MethodCall methodCall, Result result) {
String url = (String) methodCall.arguments;
webView.loadUrl("file:///android_asset/flutter_assets/" + url);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use registrar.lookupKeyForAsset

result.success(null);
}

private void canGoBack(Result result) {
result.success(webView.canGoBack());
}
Expand Down
4 changes: 2 additions & 2 deletions packages/webview_flutter/ios/Classes/FlutterWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ NS_ASSUME_NONNULL_BEGIN
- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
registrar:(NSObject<FlutterPluginRegistrar>*)registrar;

- (UIView*)view;
@end

@interface FLTWebViewFactory : NSObject <FlutterPlatformViewFactory>
- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger;
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar;
@end

NS_ASSUME_NONNULL_END
48 changes: 42 additions & 6 deletions packages/webview_flutter/ios/Classes/FlutterWebView.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
#import "JavaScriptChannelHandler.h"

@implementation FLTWebViewFactory {
NSObject<FlutterPluginRegistrar>* _registrar;
NSObject<FlutterBinaryMessenger>* _messenger;
}

- (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
- (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
self = [super init];
if (self) {
_messenger = messenger;
_registrar = registrar;
_messenger = registrar.messenger;
}
return self;
}
Expand All @@ -28,7 +30,7 @@ - (instancetype)initWithMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
FLTWebViewController* webviewController = [[FLTWebViewController alloc] initWithFrame:frame
viewIdentifier:viewId
arguments:args
binaryMessenger:_messenger];
registrar:_registrar];
return webviewController;
}

Expand All @@ -42,17 +44,20 @@ @implementation FLTWebViewController {
// The set of registered JavaScript channel names.
NSMutableSet* _javaScriptChannelNames;
FLTWKNavigationDelegate* _navigationDelegate;
NSObject<FlutterPluginRegistrar>* _registrar;
}

- (instancetype)initWithFrame:(CGRect)frame
viewIdentifier:(int64_t)viewId
arguments:(id _Nullable)args
binaryMessenger:(NSObject<FlutterBinaryMessenger>*)messenger {
registrar:(NSObject<FlutterPluginRegistrar>*)registrar {
if ([super init]) {
_viewId = viewId;
_registrar = registrar;

NSString* channelName = [NSString stringWithFormat:@"plugins.flutter.io/webview_%lld", viewId];
_channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger];
_channel = [FlutterMethodChannel methodChannelWithName:channelName
binaryMessenger:registrar.messenger];
_javaScriptChannelNames = [[NSMutableSet alloc] init];

WKUserContentController* userContentController = [[WKUserContentController alloc] init];
Expand All @@ -77,7 +82,11 @@ - (instancetype)initWithFrame:(CGRect)frame

NSString* initialUrl = args[@"initialUrl"];
if ([initialUrl isKindOfClass:[NSString class]]) {
[self loadUrl:initialUrl];
if ([initialUrl rangeOfString:@"://"].location == NSNotFound) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

[self loadAssetFile:initialUrl];
} else {
[self loadUrl:initialUrl];
}
}
}
return self;
Expand All @@ -92,6 +101,8 @@ - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
[self onUpdateSettings:call result:result];
} else if ([[call method] isEqualToString:@"loadUrl"]) {
[self onLoadUrl:call result:result];
} else if ([[call method] isEqualToString:@"loadAssetFile"]) {
[self onLoadAssetFile:call result:result];
} else if ([[call method] isEqualToString:@"canGoBack"]) {
[self onCanGoBack:call result:result];
} else if ([[call method] isEqualToString:@"canGoForward"]) {
Expand Down Expand Up @@ -133,6 +144,17 @@ - (void)onLoadUrl:(FlutterMethodCall*)call result:(FlutterResult)result {
}
}

- (void)onLoadAssetFile:(FlutterMethodCall*)call result:(FlutterResult)result {
NSString* url = [call arguments];
if (![self loadAssetFile:url]) {
result([FlutterError errorWithCode:@"loadAssetFile_failed"
message:@"Failed parsing the URL"
details:[NSString stringWithFormat:@"URL was: '%@'", url]]);
} else {
result(nil);
}
}

- (void)onCanGoBack:(FlutterMethodCall*)call result:(FlutterResult)result {
BOOL canGoBack = [_webView canGoBack];
result([NSNumber numberWithBool:canGoBack]);
Expand Down Expand Up @@ -289,6 +311,20 @@ - (bool)loadUrl:(NSString*)url withHeaders:(NSDictionary<NSString*, NSString*>*)
return true;
}

- (bool)loadAssetFile:(NSString*)url {
NSString* key = [_registrar lookupKeyForAsset:url];
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't work if the url has search or hash parameters.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It won't work if the url has search or hash parameters.

Yes.. I try to load reactjs app from local file system. it is not loading url with bookmark (#). is there any work-around to load bookmark urls?

Copy link

@kittugit kittugit Dec 10, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.. try to load bookmark url.. it is not working. . is there any work-around?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.. try to load bookmark url.. it is not working. . is there any work-around?

The returned key of method lookupKeyForAsset only can be found when the url parameter is identical to the assets path configration in pubspec.yaml.
So before calling this method, we have to remove the search and hash parts in the url string.
But we can append them back to nsUrl, the absolute path, before calling loadFileURL.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for your response.. i'm new to objective c.. tried the proposed changes you described above.. still getting same error.. My changes are in
https://github.com/kittugit/webview_flutter/blob/master/ios/Classes/FlutterWebView.m
if i use "fullKey", instead of key, i'm getting error.

from console log:
key: Frameworks/App.framework/flutter_assets/assets/build/index.html
fullKey: Frameworks/App.framework/flutter_assets/assets/build/index.html/#home

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

- (bool)loadAssetFile:(NSString*)url {
  NSArray* array = [url componentsSeparatedByString:@"?"];
  NSString* pathString = [array objectAtIndex:0];
  NSLog(@"%@%@", @"pathString: ", pathString);
  NSString* key = [_registrar lookupKeyForAsset:pathString];
  NSURL* baseURL = [[NSBundle mainBundle] URLForResource:key withExtension:nil];
  if (!baseURL) {
      return false;
   }
  NSURL* newUrl = baseURL;
  if ([array count] > 1) {
    NSString* queryString = [array objectAtIndex:1];
    NSLog(@"%@%@", @"queryString: ", queryString);
    NSString* queryPart = [NSString stringWithFormat:@"%@%@", @"?", queryString];
    NSLog(@"%@%@", @"queryPart: ", queryPart);
    newUrl = [NSURL URLWithString:queryPart relativeToURL:baseURL];
  }
  if (@available(iOS 9.0, *)) {
    [_webView loadFileURL:newUrl allowingReadAccessToURL:[NSURL URLWithString:@"file:///"]];
  } else {
    return false;
  }
  return true;
}

NSURL* nsUrl = [[NSBundle mainBundle] URLForResource:key withExtension:nil];
if (!nsUrl) {
return false;
}
if (@available(iOS 9.0, *)) {
[_webView loadFileURL:nsUrl allowingReadAccessToURL:[NSURL URLWithString:@"file:///"]];
} else {
return false;
}
return true;
}

- (void)registerJavaScriptChannels:(NSSet*)channelNames
controller:(WKUserContentController*)userContentController {
for (NSString* channelName in channelNames) {
Expand Down
3 changes: 1 addition & 2 deletions packages/webview_flutter/ios/Classes/WebViewFlutterPlugin.m
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
@implementation FLTWebViewFlutterPlugin

+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
FLTWebViewFactory* webviewFactory =
[[FLTWebViewFactory alloc] initWithMessenger:registrar.messenger];
FLTWebViewFactory* webviewFactory = [[FLTWebViewFactory alloc] initWithRegistrar:registrar];
[registrar registerViewFactory:webviewFactory withId:@"plugins.flutter.io/webview"];
[FLTCookieManager registerWithRegistrar:registrar];
}
Expand Down
11 changes: 11 additions & 0 deletions packages/webview_flutter/lib/webview_flutter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,17 @@ class WebViewController {
});
}

/// Loads the specified file.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs should say that this is a Flutter asset.

///
/// `url` must not be null.
Future<void> loadAssetFile(String url) async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: lets call it loadAsset.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't a url

assert(url != null);
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
// https://github.com/flutter/flutter/issues/26431
// ignore: strong_mode_implicit_dynamic_method
return _channel.invokeMethod('loadAssetFile', url);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@shaqian could you update the PR to clear this comment? (add type parameter)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_channel undefined name

}

/// Accessor to the current URL that the WebView is displaying.
///
/// If [WebView.initialUrl] was never specified, returns `null`.
Expand Down