diff --git a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java index a9f60ff1e6fa..893e7556c2f8 100644 --- a/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java +++ b/packages/webview_flutter/android/src/main/java/io/flutter/plugins/webviewflutter/FlutterWebView.java @@ -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("://")) { + webView.loadUrl(url); + } else { + webView.loadUrl("file:///android_asset/flutter_assets/" + url); + } } } @@ -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; @@ -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); + result.success(null); + } + private void canGoBack(Result result) { result.success(webView.canGoBack()); } diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.h b/packages/webview_flutter/ios/Classes/FlutterWebView.h index 08e6b8ab53a8..de78ed7be080 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.h +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.h @@ -12,13 +12,13 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger; + registrar:(NSObject*)registrar; - (UIView*)view; @end @interface FLTWebViewFactory : NSObject -- (instancetype)initWithMessenger:(NSObject*)messenger; +- (instancetype)initWithRegistrar:(NSObject*)registrar; @end NS_ASSUME_NONNULL_END diff --git a/packages/webview_flutter/ios/Classes/FlutterWebView.m b/packages/webview_flutter/ios/Classes/FlutterWebView.m index afc2f9921d05..d86e1ed53680 100644 --- a/packages/webview_flutter/ios/Classes/FlutterWebView.m +++ b/packages/webview_flutter/ios/Classes/FlutterWebView.m @@ -7,13 +7,15 @@ #import "JavaScriptChannelHandler.h" @implementation FLTWebViewFactory { + NSObject* _registrar; NSObject* _messenger; } -- (instancetype)initWithMessenger:(NSObject*)messenger { +- (instancetype)initWithRegistrar:(NSObject*)registrar { self = [super init]; if (self) { - _messenger = messenger; + _registrar = registrar; + _messenger = registrar.messenger; } return self; } @@ -28,7 +30,7 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { FLTWebViewController* webviewController = [[FLTWebViewController alloc] initWithFrame:frame viewIdentifier:viewId arguments:args - binaryMessenger:_messenger]; + registrar:_registrar]; return webviewController; } @@ -42,17 +44,20 @@ @implementation FLTWebViewController { // The set of registered JavaScript channel names. NSMutableSet* _javaScriptChannelNames; FLTWKNavigationDelegate* _navigationDelegate; + NSObject* _registrar; } - (instancetype)initWithFrame:(CGRect)frame viewIdentifier:(int64_t)viewId arguments:(id _Nullable)args - binaryMessenger:(NSObject*)messenger { + registrar:(NSObject*)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]; @@ -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) { + [self loadAssetFile:initialUrl]; + } else { + [self loadUrl:initialUrl]; + } } } return self; @@ -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"]) { @@ -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]); @@ -289,6 +311,20 @@ - (bool)loadUrl:(NSString*)url withHeaders:(NSDictionary*) return true; } +- (bool)loadAssetFile:(NSString*)url { + NSString* key = [_registrar lookupKeyForAsset:url]; + 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) { diff --git a/packages/webview_flutter/ios/Classes/WebViewFlutterPlugin.m b/packages/webview_flutter/ios/Classes/WebViewFlutterPlugin.m index cab9169df00a..d2859a3d5d7c 100644 --- a/packages/webview_flutter/ios/Classes/WebViewFlutterPlugin.m +++ b/packages/webview_flutter/ios/Classes/WebViewFlutterPlugin.m @@ -5,8 +5,7 @@ @implementation FLTWebViewFlutterPlugin + (void)registerWithRegistrar:(NSObject*)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]; } diff --git a/packages/webview_flutter/lib/webview_flutter.dart b/packages/webview_flutter/lib/webview_flutter.dart index 2f5bffc6704e..45a94c916029 100644 --- a/packages/webview_flutter/lib/webview_flutter.dart +++ b/packages/webview_flutter/lib/webview_flutter.dart @@ -429,6 +429,17 @@ class WebViewController { }); } + /// Loads the specified file. + /// + /// `url` must not be null. + Future loadAssetFile(String url) async { + 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); + } + /// Accessor to the current URL that the WebView is displaying. /// /// If [WebView.initialUrl] was never specified, returns `null`.