Added support for pull-to-refresh feature (fix #395), Fixed issue not rendering WebView content when scrolling on iOS (fix #704), Fixed InAppBrowser.openData method, InAppBrowser.initialUserScripts InAppBrowser.id HeadlessInAppWebView.id properties are final now
This commit is contained in:
parent
da94374dda
commit
b6d4fb9596
|
@ -133,7 +133,7 @@
|
|||
<entry key="file">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0/lib" />
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.0/lib" />
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0-nullsafety.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
|
@ -377,7 +377,7 @@
|
|||
<entry key="process">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0/lib" />
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.1.0/lib" />
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0-nullsafety.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
|
@ -441,7 +441,7 @@
|
|||
<entry key="source_span">
|
||||
<value>
|
||||
<list>
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0/lib" />
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib" />
|
||||
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0-nullsafety.4/lib" />
|
||||
</list>
|
||||
</value>
|
||||
|
@ -647,7 +647,7 @@
|
|||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/fake_async-1.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/ffi-1.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0-nullsafety.4/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.1.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/glob-1.2.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.2.0/lib" />
|
||||
|
@ -682,7 +682,7 @@
|
|||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/plugin_platform_interface-1.1.0-nullsafety.1/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pool-1.5.0-nullsafety.3/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0-nullsafety.4/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.1.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/pub_semver-1.4.4/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf-0.7.5/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-2.0.0/lib" />
|
||||
|
@ -691,7 +691,7 @@
|
|||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_map_stack_trace-2.1.0-nullsafety.4/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_maps-0.10.10-nullsafety.3/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0-nullsafety.4/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.1/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0-nullsafety.6/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stack_trace-1.10.0/lib" />
|
||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/stream_channel-2.1.0-nullsafety.3/lib" />
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
## 5.1.0
|
||||
|
||||
- Added support for pull-to-refresh feature [#395](https://github.com/pichillilorenzo/flutter_inappwebview/issues/395)
|
||||
- Fixed issue not rendering WebView content when scrolling on iOS [#703](https://github.com/pichillilorenzo/flutter_inappwebview/issues/703)
|
||||
- Fixed `InAppBrowser.openData` method
|
||||
- `InAppBrowser.initialUserScripts`, `InAppBrowser.id`, `HeadlessInAppWebView.id` properties are `final` now
|
||||
|
||||
## 5.0.5+3
|
||||
|
||||
- Fixed Android `evaluateJavascript` method when using `contentWorld: ContentWorld.PAGE`
|
||||
|
|
237
README.md
237
README.md
|
@ -302,15 +302,21 @@ Use `InAppWebViewController` to control the WebView instance.
|
|||
Example:
|
||||
```dart
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
Future main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
||||
}
|
||||
|
||||
runApp(new MyApp());
|
||||
}
|
||||
|
||||
|
@ -321,13 +327,42 @@ class MyApp extends StatefulWidget {
|
|||
|
||||
class _MyAppState extends State<MyApp> {
|
||||
|
||||
InAppWebViewController? webView;
|
||||
final GlobalKey webViewKey = GlobalKey();
|
||||
|
||||
InAppWebViewController? webViewController;
|
||||
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(
|
||||
mediaPlaybackRequiresUserGesture: false,
|
||||
),
|
||||
android: AndroidInAppWebViewOptions(
|
||||
useHybridComposition: true,
|
||||
),
|
||||
ios: IOSInAppWebViewOptions(
|
||||
allowsInlineMediaPlayback: true,
|
||||
));
|
||||
|
||||
late PullToRefreshController pullToRefreshController;
|
||||
String url = "";
|
||||
double progress = 0;
|
||||
final urlController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
pullToRefreshController = PullToRefreshController(
|
||||
options: PullToRefreshOptions(
|
||||
color: Colors.blue,
|
||||
),
|
||||
onRefresh: () async {
|
||||
if (Platform.isAndroid) {
|
||||
webViewController?.reload();
|
||||
} else if (Platform.isIOS) {
|
||||
webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(url: await webViewController?.getUrl()));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -339,87 +374,131 @@ class _MyAppState extends State<MyApp> {
|
|||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
home: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('InAppWebView Example'),
|
||||
),
|
||||
body: Container(
|
||||
child: Column(children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: progress < 1.0
|
||||
? LinearProgressIndicator(value: progress)
|
||||
: Container()),
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(10.0),
|
||||
decoration:
|
||||
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
|
||||
child: InAppWebView(
|
||||
initialUrlRequest: URLRequest(
|
||||
url: Uri.parse("https://flutter.dev/")
|
||||
),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(
|
||||
appBar: AppBar(title: Text("Official InAppWebView website")),
|
||||
body: SafeArea(
|
||||
child: Column(children: <Widget>[
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(Icons.search)
|
||||
),
|
||||
controller: urlController,
|
||||
keyboardType: TextInputType.url,
|
||||
onSubmitted: (value) {
|
||||
var url = Uri.parse(value);
|
||||
if (url.scheme.isEmpty) {
|
||||
url = Uri.parse("https://www.google.com/search?q=" + value);
|
||||
}
|
||||
webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(url: url));
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
InAppWebView(
|
||||
key: webViewKey,
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse("https://inappwebview.dev/")),
|
||||
initialOptions: options,
|
||||
pullToRefreshController: pullToRefreshController,
|
||||
onWebViewCreated: (controller) {
|
||||
webViewController = controller;
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
androidOnPermissionRequest: (InAppWebViewController controller,
|
||||
String origin, List<String> resources) async {
|
||||
return PermissionRequestResponse(
|
||||
resources: resources,
|
||||
action: PermissionRequestResponseAction.GRANT);
|
||||
},
|
||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
||||
var uri = navigationAction.request.url!;
|
||||
|
||||
),
|
||||
ios: IOSInAppWebViewOptions(
|
||||
if (![
|
||||
"http",
|
||||
"https",
|
||||
"file",
|
||||
"chrome",
|
||||
"data",
|
||||
"javascript",
|
||||
"about"
|
||||
].contains(uri.scheme)) {
|
||||
if (await canLaunch(url)) {
|
||||
// Launch the App
|
||||
await launch(
|
||||
url,
|
||||
);
|
||||
// and cancel the request
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
pullToRefreshController.endRefreshing();
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
onLoadError: (controller, url, code, message) {
|
||||
pullToRefreshController.endRefreshing();
|
||||
},
|
||||
onProgressChanged: (controller, progress) {
|
||||
if (progress == 100) {
|
||||
pullToRefreshController.endRefreshing();
|
||||
}
|
||||
setState(() {
|
||||
this.progress = progress / 100;
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
onConsoleMessage: (controller, consoleMessage) {
|
||||
print(consoleMessage);
|
||||
},
|
||||
),
|
||||
android: AndroidInAppWebViewOptions(
|
||||
useHybridComposition: true
|
||||
)
|
||||
),
|
||||
onWebViewCreated: (InAppWebViewController controller) {
|
||||
webView = controller;
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
setState(() {
|
||||
this.url = url?.toString() ?? '';
|
||||
});
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
setState(() {
|
||||
this.url = url?.toString() ?? '';
|
||||
});
|
||||
},
|
||||
onProgressChanged: (controller, progress) {
|
||||
setState(() {
|
||||
this.progress = progress / 100;
|
||||
});
|
||||
},
|
||||
progress < 1.0
|
||||
? LinearProgressIndicator(value: progress)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
ButtonBar(
|
||||
alignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
webView?.goBack();
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.arrow_forward),
|
||||
onPressed: () {
|
||||
webView?.goForward();
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
webView?.reload();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
])),
|
||||
),
|
||||
ButtonBar(
|
||||
alignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
webViewController?.goBack();
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.arrow_forward),
|
||||
onPressed: () {
|
||||
webViewController?.goForward();
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
webViewController?.reload();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
]))),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -428,11 +507,11 @@ class _MyAppState extends State<MyApp> {
|
|||
Screenshots:
|
||||
- Android:
|
||||
|
||||
![android](https://user-images.githubusercontent.com/5956938/47271038-7aebda80-d574-11e8-98fd-41e6bbc9fe2d.gif)
|
||||
![android](https://user-images.githubusercontent.com/5956938/110179602-a18ad300-7e08-11eb-849b-2c7f1af28155.gif)
|
||||
|
||||
- iOS:
|
||||
|
||||
![ios](https://user-images.githubusercontent.com/5956938/54096363-e1e72000-43ab-11e9-85c2-983a830ab7a0.gif)
|
||||
![ios](https://user-images.githubusercontent.com/5956938/110179614-a8194a80-7e08-11eb-85f9-3da10acbbcb2.gif)
|
||||
|
||||
#### `InAppWebViewController` Methods
|
||||
|
||||
|
|
|
@ -49,5 +49,6 @@ android {
|
|||
implementation 'androidx.browser:browser:1.2.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0-rc02'
|
||||
implementation 'com.squareup.okhttp3:mockwebserver:3.14.7'
|
||||
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ import com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebViewOptio
|
|||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
|
||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshOptions;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||
|
@ -48,6 +50,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
|||
public Integer windowId;
|
||||
public String id;
|
||||
public InAppWebView webView;
|
||||
public PullToRefreshLayout pullToRefreshLayout;
|
||||
public ActionBar actionBar;
|
||||
public Menu menu;
|
||||
public SearchView searchView;
|
||||
|
@ -74,6 +77,15 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
|||
|
||||
setContentView(R.layout.activity_web_view);
|
||||
|
||||
Map<String, Object> pullToRefreshInitialOptions = (Map<String, Object>) b.getSerializable("pullToRefreshInitialOptions");
|
||||
MethodChannel pullToRefreshLayoutChannel = new MethodChannel(Shared.messenger, "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id);
|
||||
PullToRefreshOptions pullToRefreshOptions = new PullToRefreshOptions();
|
||||
pullToRefreshOptions.parse(pullToRefreshInitialOptions);
|
||||
pullToRefreshLayout = findViewById(R.id.pullToRefresh);
|
||||
pullToRefreshLayout.channel = pullToRefreshLayoutChannel;
|
||||
pullToRefreshLayout.options = pullToRefreshOptions;
|
||||
pullToRefreshLayout.prepare();
|
||||
|
||||
webView = findViewById(R.id.webView);
|
||||
webView.windowId = windowId;
|
||||
webView.inAppBrowserDelegate = this;
|
||||
|
@ -128,10 +140,10 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
|||
}
|
||||
}
|
||||
else if (initialData != null) {
|
||||
String mimeType = b.getString("mimeType");
|
||||
String encoding = b.getString("encoding");
|
||||
String baseUrl = b.getString("baseUrl");
|
||||
String historyUrl = b.getString("historyUrl");
|
||||
String mimeType = b.getString("initialMimeType");
|
||||
String encoding = b.getString("initialEncoding");
|
||||
String baseUrl = b.getString("initialBaseUrl");
|
||||
String historyUrl = b.getString("initialHistoryUrl");
|
||||
webView.loadDataWithBaseURL(baseUrl, initialData, mimeType, encoding, historyUrl);
|
||||
}
|
||||
else if (initialUrlRequest != null) {
|
||||
|
|
|
@ -62,44 +62,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
|
|||
final Activity activity = Shared.activity;
|
||||
|
||||
switch (call.method) {
|
||||
case "openUrlRequest":
|
||||
{
|
||||
String id = (String) call.argument("id");
|
||||
Map<String, Object> urlRequest = (Map<String, Object>) call.argument("urlRequest");
|
||||
Map<String, Object> options = (Map<String, Object>) call.argument("options");
|
||||
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
|
||||
Integer windowId = (Integer) call.argument("windowId");
|
||||
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
|
||||
openUrlRequest(activity, id, urlRequest, options, contextMenu, windowId, initialUserScripts);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "openFile":
|
||||
{
|
||||
String id = (String) call.argument("id");
|
||||
String assetFilePath = (String) call.argument("assetFilePath");
|
||||
Map<String, Object> options = (Map<String, Object>) call.argument("options");
|
||||
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
|
||||
Integer windowId = (Integer) call.argument("windowId");
|
||||
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
|
||||
openFile(activity, id, assetFilePath, options, contextMenu, windowId, initialUserScripts);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "openData":
|
||||
{
|
||||
String id = (String) call.argument("id");
|
||||
Map<String, Object> options = (Map<String, Object>) call.argument("options");
|
||||
String data = (String) call.argument("data");
|
||||
String mimeType = (String) call.argument("mimeType");
|
||||
String encoding = (String) call.argument("encoding");
|
||||
String baseUrl = (String) call.argument("baseUrl");
|
||||
String historyUrl = (String) call.argument("historyUrl");
|
||||
Map<String, Object> contextMenu = (Map<String, Object>) call.argument("contextMenu");
|
||||
Integer windowId = (Integer) call.argument("windowId");
|
||||
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) call.argument("initialUserScripts");
|
||||
openData(activity, id, options, data, mimeType, encoding, baseUrl, historyUrl, contextMenu, windowId, initialUserScripts);
|
||||
}
|
||||
case "open":
|
||||
open(activity, (Map<String, Object>) call.arguments());
|
||||
result.success(true);
|
||||
break;
|
||||
case "openWithSystemBrowser":
|
||||
|
@ -189,45 +153,36 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
public void openUrlRequest(Activity activity, String id, Map<String, Object> urlRequest, Map<String, Object> options,
|
||||
Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
|
||||
public void open(Activity activity, Map<String, Object> arguments) {
|
||||
String id = (String) arguments.get("id");
|
||||
Map<String, Object> urlRequest = (Map<String, Object>) arguments.get("urlRequest");
|
||||
String assetFilePath = (String) arguments.get("assetFilePath");
|
||||
String data = (String) arguments.get("data");
|
||||
String mimeType = (String) arguments.get("mimeType");
|
||||
String encoding = (String) arguments.get("encoding");
|
||||
String baseUrl = (String) arguments.get("baseUrl");
|
||||
String historyUrl = (String) arguments.get("historyUrl");
|
||||
Map<String, Object> options = (Map<String, Object>) arguments.get("options");
|
||||
Map<String, Object> contextMenu = (Map<String, Object>) arguments.get("contextMenu");
|
||||
Integer windowId = (Integer) arguments.get("windowId");
|
||||
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) arguments.get("initialUserScripts");
|
||||
Map<String, Object> pullToRefreshInitialOptions = (Map<String, Object>) arguments.get("pullToRefreshOptions");
|
||||
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("fromActivity", activity.getClass().getName());
|
||||
extras.putSerializable("initialUrlRequest", (Serializable) urlRequest);
|
||||
extras.putString("id", id);
|
||||
extras.putSerializable("options", (Serializable) options);
|
||||
extras.putSerializable("contextMenu", (Serializable) contextMenu);
|
||||
extras.putInt("windowId", windowId != null ? windowId : -1);
|
||||
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
|
||||
startInAppBrowserActivity(activity, extras);
|
||||
}
|
||||
|
||||
public void openFile(Activity activity, String id, String assetFilePath, Map<String, Object> options,
|
||||
Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("fromActivity", activity.getClass().getName());
|
||||
extras.putString("initialFile", assetFilePath);
|
||||
extras.putString("id", id);
|
||||
extras.putSerializable("options", (Serializable) options);
|
||||
extras.putSerializable("contextMenu", (Serializable) contextMenu);
|
||||
extras.putInt("windowId", windowId != null ? windowId : -1);
|
||||
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
|
||||
startInAppBrowserActivity(activity, extras);
|
||||
}
|
||||
|
||||
public void openData(Activity activity, String id, Map<String, Object> options, String data, String mimeType, String encoding,
|
||||
String baseUrl, String historyUrl, Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
|
||||
Bundle extras = new Bundle();
|
||||
extras.putString("id", id);
|
||||
extras.putSerializable("options", (Serializable) options);
|
||||
extras.putString("initialData", data);
|
||||
extras.putString("initialMimeType", mimeType);
|
||||
extras.putString("initialEncoding", encoding);
|
||||
extras.putString("initialBaseUrl", baseUrl);
|
||||
extras.putString("initialHistoryUrl", historyUrl);
|
||||
extras.putString("id", id);
|
||||
extras.putSerializable("options", (Serializable) options);
|
||||
extras.putSerializable("contextMenu", (Serializable) contextMenu);
|
||||
extras.putInt("windowId", windowId != null ? windowId : -1);
|
||||
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
|
||||
extras.putSerializable("pullToRefreshInitialOptions", (Serializable) pullToRefreshInitialOptions);
|
||||
startInAppBrowserActivity(activity, extras);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ public class InAppBrowserOptions implements Options<InAppBrowserActivity> {
|
|||
public Boolean hidden = false;
|
||||
public Boolean hideToolbarTop = false;
|
||||
@Nullable
|
||||
public String toolbarTopBackgroundColor = null;
|
||||
public String toolbarTopBackgroundColor;
|
||||
@Nullable
|
||||
public String toolbarTopFixedTitle;
|
||||
public Boolean hideUrlBar = false;
|
||||
|
|
|
@ -35,7 +35,7 @@ public class ContextMenuOptions implements Options<Object> {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getRealOptions(Object webView) {
|
||||
public Map<String, Object> getRealOptions(Object obj) {
|
||||
Map<String, Object> realOptions = toMap();
|
||||
return realOptions;
|
||||
}
|
||||
|
|
|
@ -16,9 +16,10 @@ import androidx.webkit.WebViewFeature;
|
|||
|
||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
|
||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout;
|
||||
import com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshOptions;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.URLRequest;
|
||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -38,6 +39,7 @@ public class FlutterWebView implements PlatformView {
|
|||
public InAppWebView webView;
|
||||
public final MethodChannel channel;
|
||||
public InAppWebViewMethodHandler methodCallDelegate;
|
||||
public PullToRefreshLayout pullToRefreshLayout;
|
||||
|
||||
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) {
|
||||
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
|
||||
|
@ -53,6 +55,7 @@ public class FlutterWebView implements PlatformView {
|
|||
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
|
||||
Integer windowId = (Integer) params.get("windowId");
|
||||
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) params.get("initialUserScripts");
|
||||
Map<String, Object> pullToRefreshInitialOptions = (Map<String, Object>) params.get("pullToRefreshOptions");
|
||||
|
||||
InAppWebViewOptions options = new InAppWebViewOptions();
|
||||
options.parse(initialOptions);
|
||||
|
@ -74,6 +77,13 @@ public class FlutterWebView implements PlatformView {
|
|||
webView = new InAppWebView(context, channel, id, windowId, options, contextMenu, containerView, userScripts);
|
||||
displayListenerProxy.onPostWebViewInitialization(displayManager);
|
||||
|
||||
MethodChannel pullToRefreshLayoutChannel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id);
|
||||
PullToRefreshOptions pullToRefreshOptions = new PullToRefreshOptions();
|
||||
pullToRefreshOptions.parse(pullToRefreshInitialOptions);
|
||||
pullToRefreshLayout = new PullToRefreshLayout(context, pullToRefreshLayoutChannel, pullToRefreshOptions);
|
||||
pullToRefreshLayout.addView(webView);
|
||||
pullToRefreshLayout.prepare();
|
||||
|
||||
methodCallDelegate = new InAppWebViewMethodHandler(webView);
|
||||
channel.setMethodCallHandler(methodCallDelegate);
|
||||
|
||||
|
@ -117,7 +127,7 @@ public class FlutterWebView implements PlatformView {
|
|||
|
||||
@Override
|
||||
public View getView() {
|
||||
return webView;
|
||||
return pullToRefreshLayout;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -145,6 +155,11 @@ public class FlutterWebView implements PlatformView {
|
|||
webView.dispose();
|
||||
webView.destroy();
|
||||
webView = null;
|
||||
|
||||
if (pullToRefreshLayout != null) {
|
||||
pullToRefreshLayout.dispose();
|
||||
pullToRefreshLayout = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
WebSettings settings = webView.getSettings();
|
||||
|
|
|
@ -175,7 +175,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
|
||||
channel.invokeMethod("onJsAlert", obj, new MethodChannel.Result() {
|
||||
@Override
|
||||
public void success(Object response) {
|
||||
public void success(@Nullable Object response) {
|
||||
String responseMessage = null;
|
||||
String confirmButtonTitle = null;
|
||||
|
||||
|
@ -203,8 +203,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
result.cancel();
|
||||
}
|
||||
|
||||
|
@ -290,8 +290,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
result.cancel();
|
||||
}
|
||||
|
||||
|
@ -393,8 +393,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
result.cancel();
|
||||
}
|
||||
|
||||
|
@ -507,8 +507,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
result.cancel();
|
||||
}
|
||||
|
||||
|
@ -599,6 +599,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
|
||||
@Override
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
if (InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
|
||||
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
|
||||
}
|
||||
|
@ -638,7 +639,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
callback.invoke(origin, false, false);
|
||||
}
|
||||
|
||||
|
@ -1124,8 +1126,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
request.deny();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -150,8 +150,8 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, "ERROR: " + s + " " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
|
||||
}
|
||||
|
||||
|
@ -375,8 +375,8 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -428,8 +428,8 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -493,8 +493,8 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -553,8 +553,8 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void error(String s, String s1, Object o) {
|
||||
Log.e(LOG_TAG, s + ", " + s1);
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -736,7 +736,7 @@ public class InAppWebViewClient extends WebViewClient {
|
|||
|
||||
@Override
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.d(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
|
||||
Log.e(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -47,7 +47,7 @@ public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient
|
|||
|
||||
@Override
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.d(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -79,7 +79,7 @@ public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient
|
|||
|
||||
@Override
|
||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||
Log.d(LOG_TAG, "ERROR: " + errorCode + " " + errorMessage);
|
||||
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.pull_to_refresh;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import io.flutter.plugin.common.MethodCall;
|
||||
import io.flutter.plugin.common.MethodChannel;
|
||||
|
||||
public class PullToRefreshLayout extends SwipeRefreshLayout implements MethodChannel.MethodCallHandler {
|
||||
static final String LOG_TAG = "PullToRefreshLayout";
|
||||
|
||||
public MethodChannel channel;
|
||||
public PullToRefreshOptions options;
|
||||
|
||||
public PullToRefreshLayout(@NonNull Context context, @NonNull MethodChannel channel, @NonNull PullToRefreshOptions options) {
|
||||
super(context);
|
||||
this.channel = channel;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public PullToRefreshLayout(@NonNull Context context) {
|
||||
super(context);
|
||||
this.channel = null;
|
||||
this.options = null;
|
||||
}
|
||||
|
||||
public PullToRefreshLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.channel = null;
|
||||
this.options = null;
|
||||
}
|
||||
|
||||
public void prepare() {
|
||||
final PullToRefreshLayout self = this;
|
||||
|
||||
if (channel != null) {
|
||||
this.channel.setMethodCallHandler(this);
|
||||
}
|
||||
|
||||
setEnabled(options.enabled);
|
||||
setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
|
||||
@Override
|
||||
public void onRefresh() {
|
||||
if (channel == null) {
|
||||
self.setRefreshing(false);
|
||||
return;
|
||||
}
|
||||
Map<String, Object> obj = new HashMap<>();
|
||||
channel.invokeMethod("onRefresh", obj);
|
||||
}
|
||||
});
|
||||
if (options.color != null)
|
||||
setColorSchemeColors(Color.parseColor(options.color));
|
||||
if (options.backgroundColor != null)
|
||||
setProgressBackgroundColorSchemeColor(Color.parseColor(options.backgroundColor));
|
||||
if (options.distanceToTriggerSync != null)
|
||||
setDistanceToTriggerSync(options.distanceToTriggerSync);
|
||||
if (options.slingshotDistance != null)
|
||||
setSlingshotDistance(options.slingshotDistance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMethodCall(@NonNull MethodCall call, @NonNull final MethodChannel.Result result) {
|
||||
switch (call.method) {
|
||||
case "setEnabled":
|
||||
{
|
||||
Boolean enabled = (Boolean) call.argument("enabled");
|
||||
setEnabled(enabled);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "setRefreshing":
|
||||
{
|
||||
Boolean refreshing = (Boolean) call.argument("refreshing");
|
||||
setRefreshing(refreshing);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "isRefreshing":
|
||||
result.success(isRefreshing());
|
||||
break;
|
||||
case "setColor":
|
||||
{
|
||||
String color = (String) call.argument("color");
|
||||
setColorSchemeColors(Color.parseColor(color));
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "setBackgroundColor":
|
||||
{
|
||||
String color = (String) call.argument("color");
|
||||
setProgressBackgroundColorSchemeColor(Color.parseColor(color));
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "setDistanceToTriggerSync":
|
||||
{
|
||||
Integer distanceToTriggerSync = (Integer) call.argument("distanceToTriggerSync");
|
||||
setDistanceToTriggerSync(distanceToTriggerSync);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "setSlingshotDistance":
|
||||
{
|
||||
Integer slingshotDistance = (Integer) call.argument("slingshotDistance");
|
||||
setSlingshotDistance(slingshotDistance);
|
||||
}
|
||||
result.success(true);
|
||||
break;
|
||||
case "getDefaultSlingshotDistance":
|
||||
result.success(SwipeRefreshLayout.DEFAULT_SLINGSHOT_DISTANCE);
|
||||
break;
|
||||
default:
|
||||
result.notImplemented();
|
||||
}
|
||||
}
|
||||
|
||||
public void dispose() {
|
||||
removeAllViews();
|
||||
if (channel != null) {
|
||||
channel.setMethodCallHandler(null);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package com.pichillilorenzo.flutter_inappwebview.pull_to_refresh;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.pichillilorenzo.flutter_inappwebview.Options;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PullToRefreshOptions implements Options<PullToRefreshLayout> {
|
||||
public static final String LOG_TAG = "PullToRefreshOptions";
|
||||
|
||||
public Boolean enabled = true;
|
||||
@Nullable
|
||||
public String color;
|
||||
@Nullable
|
||||
public String backgroundColor;
|
||||
@Nullable
|
||||
public Integer distanceToTriggerSync;
|
||||
@Nullable
|
||||
public Integer slingshotDistance;
|
||||
|
||||
public PullToRefreshOptions parse(Map<String, Object> options) {
|
||||
for (Map.Entry<String, Object> pair : options.entrySet()) {
|
||||
String key = pair.getKey();
|
||||
Object value = pair.getValue();
|
||||
if (value == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "enabled":
|
||||
enabled = (Boolean) value;
|
||||
break;
|
||||
case "color":
|
||||
color = (String) value;
|
||||
break;
|
||||
case "backgroundColor":
|
||||
backgroundColor = (String) value;
|
||||
break;
|
||||
case "distanceToTriggerSync":
|
||||
distanceToTriggerSync = (Integer) value;
|
||||
break;
|
||||
case "slingshotDistance":
|
||||
slingshotDistance = (Integer) value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> toMap() {
|
||||
Map<String, Object> options = new HashMap<>();
|
||||
options.put("enabled", enabled);
|
||||
options.put("color", color);
|
||||
options.put("backgroundColor", backgroundColor);
|
||||
options.put("distanceToTriggerSync", distanceToTriggerSync);
|
||||
options.put("slingshotDistance", slingshotDistance);
|
||||
return options;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getRealOptions(PullToRefreshLayout pullToRefreshLayout) {
|
||||
Map<String, Object> realOptions = toMap();
|
||||
return realOptions;
|
||||
}
|
||||
|
||||
}
|
|
@ -10,10 +10,15 @@
|
|||
tools:context=".in_app_browser.InAppBrowserActivity"
|
||||
android:focusable="true">
|
||||
|
||||
<com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView
|
||||
android:id="@+id/webView"
|
||||
<com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout
|
||||
android:id="@+id/pullToRefresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
android:layout_height="match_parent">
|
||||
<com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView
|
||||
android:id="@+id/webView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progressBar"
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-03-02 01:01:13.621928","version":"1.27.0-4.0.pre"}
|
||||
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"android":[{"name":"flutter_downloader","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/flutter_downloader-1.5.2/","dependencies":[]},{"name":"flutter_inappwebview","path":"/Users/lorenzopichilli/Desktop/flutter_inappwebview/","dependencies":[]},{"name":"integration_test","path":"/Users/lorenzopichilli/flutter/packages/integration_test/","dependencies":[]},{"name":"path_provider","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider-2.0.0-nullsafety/","dependencies":[]},{"name":"permission_handler","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/permission_handler-5.1.0+2/","dependencies":[]},{"name":"url_launcher","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher-6.0.0-nullsafety.6/","dependencies":[]}],"macos":[{"name":"path_provider_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_macos-0.0.5-nullsafety/","dependencies":[]},{"name":"url_launcher_macos","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_macos-0.1.0-nullsafety.2/","dependencies":[]}],"linux":[{"name":"path_provider_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_linux-0.2.0-nullsafety/","dependencies":[]},{"name":"url_launcher_linux","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_linux-0.1.0-nullsafety.3/","dependencies":[]}],"windows":[{"name":"path_provider_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/path_provider_windows-0.1.0-nullsafety.3/","dependencies":[]},{"name":"url_launcher_windows","path":"/Users/lorenzopichilli/flutter/.pub-cache/hosted/pub.dartlang.org/url_launcher_windows-0.1.0-nullsafety.2/","dependencies":[]}],"web":[]},"dependencyGraph":[{"name":"flutter_downloader","dependencies":[]},{"name":"flutter_inappwebview","dependencies":[]},{"name":"integration_test","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_macos","path_provider_linux","path_provider_windows"]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_macos","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"permission_handler","dependencies":[]},{"name":"url_launcher","dependencies":["url_launcher_linux","url_launcher_macos","url_launcher_windows"]},{"name":"url_launcher_linux","dependencies":[]},{"name":"url_launcher_macos","dependencies":[]},{"name":"url_launcher_windows","dependencies":[]}],"date_created":"2021-03-05 22:47:49.023558","version":"2.1.0-10.0.pre"}
|
|
@ -86,7 +86,7 @@ void main() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -96,7 +96,7 @@ void main() {
|
|||
final InAppWebViewController controller =
|
||||
await controllerCompleter.future;
|
||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
||||
expect(currentUrl, 'https://flutter.dev/');
|
||||
expect(currentUrl, 'https://github.com/flutter');
|
||||
});
|
||||
|
||||
testWidgets('set/get options', (WidgetTester tester) async {
|
||||
|
@ -109,7 +109,7 @@ void main() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
||||
onWebViewCreated: (controller) {
|
||||
|
@ -318,7 +318,7 @@ void main() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -331,7 +331,7 @@ void main() {
|
|||
final InAppWebViewController controller =
|
||||
await controllerCompleter.future;
|
||||
var url = await pageLoads.stream.first;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
|
||||
await controller.loadUrl(
|
||||
urlRequest: URLRequest(url: Uri.parse('https://www.google.com/')));
|
||||
|
@ -353,7 +353,7 @@ void main() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -1528,7 +1528,7 @@ void main() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
ios: IOSInAppWebViewOptions(
|
||||
allowsBackForwardNavigationGestures: true)),
|
||||
|
@ -1542,8 +1542,8 @@ void main() {
|
|||
final InAppWebViewController controller =
|
||||
await controllerCompleter.future;
|
||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
||||
expect(currentUrl, contains('flutter.dev'));
|
||||
});
|
||||
expect(currentUrl, 'https://github.com/flutter');
|
||||
}, skip: !Platform.isIOS);
|
||||
|
||||
testWidgets('target _blank opens in same window',
|
||||
(WidgetTester tester) async {
|
||||
|
@ -2497,7 +2497,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(
|
||||
clearCache: true,
|
||||
|
@ -2560,7 +2560,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -2634,7 +2634,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onLoadStop: (controller, url) async {
|
||||
await controller.evaluateJavascript(source: "window.print();");
|
||||
},
|
||||
|
@ -2645,7 +2645,7 @@ setTimeout(function() {
|
|||
),
|
||||
);
|
||||
final String url = await onPrintCompleter.future;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
}, skip: true);
|
||||
|
||||
testWidgets('onWindowFocus', (WidgetTester tester) async {
|
||||
|
@ -2656,7 +2656,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onLoadStop: (controller, url) async {
|
||||
await controller.evaluateJavascript(
|
||||
source: 'window.dispatchEvent(new Event("focus"));');
|
||||
|
@ -2678,7 +2678,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onLoadStop: (controller, url) async {
|
||||
await controller.evaluateJavascript(
|
||||
source: 'window.dispatchEvent(new Event("blur"));');
|
||||
|
@ -2703,7 +2703,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -2715,7 +2715,7 @@ setTimeout(function() {
|
|||
);
|
||||
|
||||
final String? url = await onPageCommitVisibleCompleter.future;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
});
|
||||
|
||||
testWidgets('onTitleChanged', (WidgetTester tester) async {
|
||||
|
@ -2729,7 +2729,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -2903,7 +2903,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -2973,7 +2973,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -3019,7 +3019,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)),
|
||||
onWebViewCreated: (controller) {
|
||||
|
@ -3039,7 +3039,7 @@ setTimeout(function() {
|
|||
|
||||
await pageLoaded.future;
|
||||
final String url = await onNavigationResponseCompleter.future;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
}, skip: !Platform.isIOS);
|
||||
|
||||
testWidgets('cancel navigation', (WidgetTester tester) async {
|
||||
|
@ -3055,7 +3055,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)),
|
||||
onWebViewCreated: (controller) {
|
||||
|
@ -3074,7 +3074,7 @@ setTimeout(function() {
|
|||
);
|
||||
|
||||
final String url = await onNavigationResponseCompleter.future;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
expect(pageLoaded.future, doesNotComplete);
|
||||
}, skip: !Platform.isIOS);
|
||||
}, skip: !Platform.isIOS);
|
||||
|
@ -3374,7 +3374,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -3387,11 +3387,11 @@ setTimeout(function() {
|
|||
final InAppWebViewController controller =
|
||||
await controllerCompleter.future;
|
||||
String? url = await pageLoads.stream.first;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
|
||||
await controller.reload();
|
||||
url = await pageLoads.stream.first;
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
|
||||
pageLoads.close();
|
||||
});
|
||||
|
@ -3443,6 +3443,7 @@ setTimeout(function() {
|
|||
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
||||
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
||||
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
await controller.goBack();
|
||||
url = await pageLoads.stream.first;
|
||||
webHistory = await controller.getCopyBackForwardList();
|
||||
|
@ -3456,6 +3457,7 @@ setTimeout(function() {
|
|||
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
||||
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
||||
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
await controller.goForward();
|
||||
url = await pageLoads.stream.first;
|
||||
webHistory = await controller.getCopyBackForwardList();
|
||||
|
@ -3469,6 +3471,7 @@ setTimeout(function() {
|
|||
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
||||
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
||||
|
||||
await Future.delayed(Duration(seconds: 1));
|
||||
await controller.goTo(historyItem: webHistory.list![0]);
|
||||
url = await pageLoads.stream.first;
|
||||
webHistory = await controller.getCopyBackForwardList();
|
||||
|
@ -4392,6 +4395,36 @@ setTimeout(function() {
|
|||
expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull);
|
||||
});
|
||||
|
||||
testWidgets('launches with pull-to-refresh feature', (WidgetTester tester) async {
|
||||
final Completer controllerCompleter = Completer<InAppWebViewController>();
|
||||
final pullToRefreshController = PullToRefreshController(
|
||||
options: PullToRefreshOptions(
|
||||
color: Colors.blue,
|
||||
),
|
||||
onRefresh: () {
|
||||
|
||||
},
|
||||
);
|
||||
|
||||
await tester.pumpWidget(
|
||||
Directionality(
|
||||
textDirection: TextDirection.ltr,
|
||||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest: URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
pullToRefreshController: pullToRefreshController,
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
final InAppWebViewController controller =
|
||||
await controllerCompleter.future;
|
||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
||||
expect(currentUrl, 'https://github.com/flutter');
|
||||
});
|
||||
|
||||
group('android methods', () {
|
||||
testWidgets('clearSslPreferences', (WidgetTester tester) async {
|
||||
final Completer controllerCompleter =
|
||||
|
@ -4462,7 +4495,7 @@ setTimeout(function() {
|
|||
child: InAppWebView(
|
||||
key: GlobalKey(),
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse('https://flutter.dev')),
|
||||
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -4477,7 +4510,7 @@ setTimeout(function() {
|
|||
await controllerCompleter.future;
|
||||
await pageLoaded.future;
|
||||
var originUrl = (await controller.android.getOriginalUrl())?.toString();
|
||||
expect(originUrl, 'https://flutter.dev/');
|
||||
expect(originUrl, 'https://github.com/flutter');
|
||||
}, skip: !Platform.isAndroid);
|
||||
|
||||
testWidgets('pageDown/pageUp', (WidgetTester tester) async {
|
||||
|
@ -4759,7 +4792,7 @@ setTimeout(function() {
|
|||
final Completer<void> pageLoaded = Completer<void>();
|
||||
|
||||
var headlessWebView = new HeadlessInAppWebView(
|
||||
initialUrlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
|
||||
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||
onWebViewCreated: (controller) {
|
||||
controllerCompleter.complete(controller);
|
||||
},
|
||||
|
@ -4774,7 +4807,7 @@ setTimeout(function() {
|
|||
await pageLoaded.future;
|
||||
|
||||
final String? url = (await controller.getUrl())?.toString();
|
||||
expect(url, 'https://flutter.dev/');
|
||||
expect(url, 'https://github.com/flutter');
|
||||
|
||||
await headlessWebView.dispose();
|
||||
|
||||
|
@ -4787,7 +4820,7 @@ setTimeout(function() {
|
|||
final Completer<void> pageLoaded = Completer<void>();
|
||||
|
||||
var headlessWebView = new HeadlessInAppWebView(
|
||||
initialUrlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
|
||||
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||
initialOptions: InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
||||
onWebViewCreated: (controller) {
|
||||
|
@ -4818,7 +4851,7 @@ setTimeout(function() {
|
|||
});
|
||||
|
||||
group('InAppBrowser', () {
|
||||
test('open and close', () async {
|
||||
test('openUrlRequest and close', () async {
|
||||
var inAppBrowser = new MyInAppBrowser();
|
||||
expect(inAppBrowser.isOpened(), false);
|
||||
expect(() async {
|
||||
|
@ -4826,13 +4859,87 @@ setTimeout(function() {
|
|||
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
||||
|
||||
await inAppBrowser.openUrlRequest(
|
||||
urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")));
|
||||
urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||
await inAppBrowser.browserCreated.future;
|
||||
expect(inAppBrowser.isOpened(), true);
|
||||
expect(() async {
|
||||
await inAppBrowser.openUrlRequest(
|
||||
urlRequest:
|
||||
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||
URLRequest(url: Uri.parse("https://flutter.dev")));
|
||||
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||
|
||||
await inAppBrowser.firstPageLoaded.future;
|
||||
var controller = inAppBrowser.webViewController;
|
||||
|
||||
final String? url = (await controller.getUrl())?.toString();
|
||||
expect(url, 'https://github.com/flutter');
|
||||
|
||||
await inAppBrowser.close();
|
||||
expect(inAppBrowser.isOpened(), false);
|
||||
expect(() async => await inAppBrowser.webViewController.getUrl(),
|
||||
throwsA(isInstanceOf<MissingPluginException>()));
|
||||
});
|
||||
|
||||
test('openFile and close', () async {
|
||||
var inAppBrowser = new MyInAppBrowser();
|
||||
expect(inAppBrowser.isOpened(), false);
|
||||
expect(() async {
|
||||
await inAppBrowser.show();
|
||||
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
||||
|
||||
await inAppBrowser.openFile(assetFilePath: "test_assets/in_app_webview_initial_file_test.html");
|
||||
await inAppBrowser.browserCreated.future;
|
||||
expect(inAppBrowser.isOpened(), true);
|
||||
expect(() async {
|
||||
await inAppBrowser.openUrlRequest(
|
||||
urlRequest:
|
||||
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||
|
||||
await inAppBrowser.firstPageLoaded.future;
|
||||
var controller = inAppBrowser.webViewController;
|
||||
|
||||
final String? url = (await controller.getUrl())?.toString();
|
||||
expect(url, endsWith("in_app_webview_initial_file_test.html"));
|
||||
|
||||
await inAppBrowser.close();
|
||||
expect(inAppBrowser.isOpened(), false);
|
||||
expect(() async => await inAppBrowser.webViewController.getUrl(),
|
||||
throwsA(isInstanceOf<MissingPluginException>()));
|
||||
});
|
||||
|
||||
test('openFile and close', () async {
|
||||
var inAppBrowser = new MyInAppBrowser();
|
||||
expect(inAppBrowser.isOpened(), false);
|
||||
expect(() async {
|
||||
await inAppBrowser.show();
|
||||
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
||||
|
||||
await inAppBrowser.openData(data: """
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
<link rel="stylesheet" href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css">
|
||||
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<img src="https://via.placeholder.com/100x50" alt="placeholder 100x50">
|
||||
</body>
|
||||
</html>
|
||||
""",
|
||||
encoding: 'utf-8',
|
||||
mimeType: 'text/html',
|
||||
androidHistoryUrl: Uri.parse("https://flutter.dev"),
|
||||
baseUrl: Uri.parse("https://flutter.dev"));
|
||||
await inAppBrowser.browserCreated.future;
|
||||
expect(inAppBrowser.isOpened(), true);
|
||||
expect(() async {
|
||||
await inAppBrowser.openUrlRequest(
|
||||
urlRequest:
|
||||
URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||
|
||||
await inAppBrowser.firstPageLoaded.future;
|
||||
|
@ -4850,7 +4957,7 @@ setTimeout(function() {
|
|||
test('set/get options', () async {
|
||||
var inAppBrowser = new MyInAppBrowser();
|
||||
await inAppBrowser.openUrlRequest(
|
||||
urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
|
||||
urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||
options: InAppBrowserClassOptions(
|
||||
crossPlatform: InAppBrowserOptions(hideToolbarTop: true)));
|
||||
await inAppBrowser.browserCreated.future;
|
||||
|
@ -4875,12 +4982,12 @@ setTimeout(function() {
|
|||
var chromeSafariBrowser = new MyChromeSafariBrowser();
|
||||
expect(chromeSafariBrowser.isOpened(), false);
|
||||
|
||||
await chromeSafariBrowser.open(url: Uri.parse("https://flutter.dev"));
|
||||
await chromeSafariBrowser.open(url: Uri.parse("https://github.com/flutter"));
|
||||
await chromeSafariBrowser.browserCreated.future;
|
||||
expect(chromeSafariBrowser.isOpened(), true);
|
||||
expect(() async {
|
||||
await chromeSafariBrowser.open(
|
||||
url: Uri.parse("https://github.com/flutter"));
|
||||
url: Uri.parse("https://flutter.dev"));
|
||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||
|
||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
|
@ -23,16 +24,21 @@ class MyInAppBrowser extends InAppBrowser {
|
|||
|
||||
@override
|
||||
Future onLoadStop(url) async {
|
||||
pullToRefreshController?.endRefreshing();
|
||||
print("\n\nStopped $url\n\n");
|
||||
}
|
||||
|
||||
@override
|
||||
void onLoadError(url, code, message) {
|
||||
pullToRefreshController?.endRefreshing();
|
||||
print("Can't load $url.. Error: $message");
|
||||
}
|
||||
|
||||
@override
|
||||
void onProgressChanged(progress) {
|
||||
if (progress == 100) {
|
||||
pullToRefreshController?.endRefreshing();
|
||||
}
|
||||
print("Progress: $progress");
|
||||
}
|
||||
|
||||
|
@ -77,9 +83,27 @@ class InAppBrowserExampleScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
||||
|
||||
late PullToRefreshController pullToRefreshController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
pullToRefreshController = PullToRefreshController(
|
||||
options: PullToRefreshOptions(
|
||||
color: Colors.black,
|
||||
),
|
||||
onRefresh: () async {
|
||||
if (Platform.isAndroid) {
|
||||
widget.browser.webViewController.reload();
|
||||
} else if (Platform.isIOS) {
|
||||
widget.browser.webViewController.loadUrl(
|
||||
urlRequest: URLRequest(url: await widget.browser.webViewController.getUrl()));
|
||||
}
|
||||
},
|
||||
);
|
||||
widget.browser.pullToRefreshController = pullToRefreshController;
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -17,13 +17,26 @@ class InAppWebViewExampleScreen extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||
|
||||
final GlobalKey webViewKey = GlobalKey();
|
||||
|
||||
InAppWebViewController? webView;
|
||||
InAppWebViewController? webViewController;
|
||||
InAppWebViewGroupOptions options = InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(
|
||||
mediaPlaybackRequiresUserGesture: false,
|
||||
),
|
||||
android: AndroidInAppWebViewOptions(
|
||||
useHybridComposition: true,
|
||||
),
|
||||
ios: IOSInAppWebViewOptions(
|
||||
allowsInlineMediaPlayback: true,
|
||||
));
|
||||
|
||||
late PullToRefreshController pullToRefreshController;
|
||||
late ContextMenu contextMenu;
|
||||
String url = "";
|
||||
double progress = 0;
|
||||
// CookieManager _cookieManager = CookieManager.instance();
|
||||
final urlController = TextEditingController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -37,15 +50,15 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
title: "Special",
|
||||
action: () async {
|
||||
print("Menu item Special clicked!");
|
||||
print(await webView?.getSelectedText());
|
||||
await webView?.clearFocus();
|
||||
print(await webViewController?.getSelectedText());
|
||||
await webViewController?.clearFocus();
|
||||
})
|
||||
],
|
||||
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: false),
|
||||
onCreateContextMenu: (hitTestResult) async {
|
||||
print("onCreateContextMenu");
|
||||
print(hitTestResult.extra);
|
||||
print(await webView?.getSelectedText());
|
||||
print(await webViewController?.getSelectedText());
|
||||
},
|
||||
onHideContextMenu: () {
|
||||
print("onHideContextMenu");
|
||||
|
@ -59,6 +72,20 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
" " +
|
||||
contextMenuItemClicked.title);
|
||||
});
|
||||
|
||||
pullToRefreshController = PullToRefreshController(
|
||||
options: PullToRefreshOptions(
|
||||
color: Colors.blue,
|
||||
),
|
||||
onRefresh: () async {
|
||||
if (Platform.isAndroid) {
|
||||
webViewController?.reload();
|
||||
} else if (Platform.isIOS) {
|
||||
webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(url: await webViewController?.getUrl()));
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -66,18 +93,6 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
super.dispose();
|
||||
}
|
||||
|
||||
var options = InAppWebViewGroupOptions(
|
||||
crossPlatform: InAppWebViewOptions(
|
||||
useShouldOverrideUrlLoading: false,
|
||||
mediaPlaybackRequiresUserGesture: false,
|
||||
),
|
||||
android: AndroidInAppWebViewOptions(
|
||||
useHybridComposition: true,
|
||||
),
|
||||
ios: IOSInAppWebViewOptions(
|
||||
allowsInlineMediaPlayback: true,
|
||||
));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
|
@ -85,92 +100,106 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
drawer: myDrawer(context: context),
|
||||
body: SafeArea(
|
||||
child: Column(children: <Widget>[
|
||||
Container(
|
||||
padding: EdgeInsets.all(20.0),
|
||||
child: Text(
|
||||
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
|
||||
),
|
||||
Container(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: progress < 1.0
|
||||
? LinearProgressIndicator(value: progress)
|
||||
: Container()),
|
||||
Expanded(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.all(10.0),
|
||||
decoration:
|
||||
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
|
||||
child: InAppWebView(
|
||||
key: webViewKey,
|
||||
// contextMenu: contextMenu,
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse("https://flutter.dev")),
|
||||
// initialFile: "assets/index.html",
|
||||
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
||||
initialOptions: options,
|
||||
onWebViewCreated: (controller) {
|
||||
webView = controller;
|
||||
print("onWebViewCreated");
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
print("onLoadStart $url");
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
});
|
||||
},
|
||||
androidOnPermissionRequest: (InAppWebViewController controller,
|
||||
String origin, List<String> resources) async {
|
||||
return PermissionRequestResponse(
|
||||
resources: resources,
|
||||
action: PermissionRequestResponseAction.GRANT);
|
||||
},
|
||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
||||
var uri = navigationAction.request.url!;
|
||||
|
||||
if (![
|
||||
"http",
|
||||
"https",
|
||||
"file",
|
||||
"chrome",
|
||||
"data",
|
||||
"javascript",
|
||||
"about"
|
||||
].contains(uri.scheme)) {
|
||||
if (await canLaunch(url)) {
|
||||
// Launch the App
|
||||
await launch(
|
||||
url,
|
||||
);
|
||||
// and cancel the request
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
print("onLoadStop $url");
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
});
|
||||
webView = controller;
|
||||
},
|
||||
onProgressChanged: (controller, progress) {
|
||||
setState(() {
|
||||
this.progress = progress / 100;
|
||||
});
|
||||
},
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
print("onUpdateVisitedHistory $url");
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
});
|
||||
},
|
||||
onConsoleMessage: (controller, consoleMessage) {
|
||||
print(consoleMessage);
|
||||
},
|
||||
),
|
||||
TextField(
|
||||
decoration: InputDecoration(
|
||||
prefixIcon: Icon(Icons.search)
|
||||
),
|
||||
controller: urlController,
|
||||
keyboardType: TextInputType.url,
|
||||
onSubmitted: (value) {
|
||||
var url = Uri.parse(value);
|
||||
if (url.scheme.isEmpty) {
|
||||
url = Uri.parse("https://www.google.com/search?q=" + value);
|
||||
}
|
||||
webViewController?.loadUrl(
|
||||
urlRequest: URLRequest(url: url));
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
InAppWebView(
|
||||
key: webViewKey,
|
||||
// contextMenu: contextMenu,
|
||||
initialUrlRequest:
|
||||
URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||
// initialFile: "assets/index.html",
|
||||
initialUserScripts: UnmodifiableListView<UserScript>([]),
|
||||
initialOptions: options,
|
||||
pullToRefreshController: pullToRefreshController,
|
||||
onWebViewCreated: (controller) {
|
||||
webViewController = controller;
|
||||
},
|
||||
onLoadStart: (controller, url) {
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
androidOnPermissionRequest: (InAppWebViewController controller,
|
||||
String origin, List<String> resources) async {
|
||||
return PermissionRequestResponse(
|
||||
resources: resources,
|
||||
action: PermissionRequestResponseAction.GRANT);
|
||||
},
|
||||
shouldOverrideUrlLoading: (controller, navigationAction) async {
|
||||
var uri = navigationAction.request.url!;
|
||||
|
||||
if (![
|
||||
"http",
|
||||
"https",
|
||||
"file",
|
||||
"chrome",
|
||||
"data",
|
||||
"javascript",
|
||||
"about"
|
||||
].contains(uri.scheme)) {
|
||||
if (await canLaunch(url)) {
|
||||
// Launch the App
|
||||
await launch(
|
||||
url,
|
||||
);
|
||||
// and cancel the request
|
||||
return NavigationActionPolicy.CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
return NavigationActionPolicy.ALLOW;
|
||||
},
|
||||
onLoadStop: (controller, url) async {
|
||||
pullToRefreshController.endRefreshing();
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
onLoadError: (controller, url, code, message) {
|
||||
pullToRefreshController.endRefreshing();
|
||||
},
|
||||
onProgressChanged: (controller, progress) {
|
||||
if (progress == 100) {
|
||||
pullToRefreshController.endRefreshing();
|
||||
}
|
||||
setState(() {
|
||||
this.progress = progress / 100;
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
onUpdateVisitedHistory: (controller, url, androidIsReload) {
|
||||
setState(() {
|
||||
this.url = url.toString();
|
||||
urlController.text = this.url;
|
||||
});
|
||||
},
|
||||
onConsoleMessage: (controller, consoleMessage) {
|
||||
print(consoleMessage);
|
||||
},
|
||||
),
|
||||
progress < 1.0
|
||||
? LinearProgressIndicator(value: progress)
|
||||
: Container(),
|
||||
],
|
||||
),
|
||||
),
|
||||
ButtonBar(
|
||||
alignment: MainAxisAlignment.center,
|
||||
|
@ -178,19 +207,19 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
|||
ElevatedButton(
|
||||
child: Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
webView?.goBack();
|
||||
webViewController?.goBack();
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.arrow_forward),
|
||||
onPressed: () {
|
||||
webView?.goForward();
|
||||
webViewController?.goForward();
|
||||
},
|
||||
),
|
||||
ElevatedButton(
|
||||
child: Icon(Icons.refresh),
|
||||
onPressed: () {
|
||||
webView?.reload();
|
||||
webViewController?.reload();
|
||||
},
|
||||
),
|
||||
],
|
||||
|
|
|
@ -22,7 +22,7 @@ dependencies:
|
|||
cupertino_icons: ^1.0.2
|
||||
flutter_downloader: ^1.5.2
|
||||
path_provider: ^2.0.0-nullsafety
|
||||
permission_handler: ^5.0.1+1
|
||||
permission_handler: ^5.1.0+2
|
||||
url_launcher: ^6.0.0-nullsafety.4
|
||||
# connectivity: ^0.4.5+6
|
||||
flutter_inappwebview:
|
||||
|
|
|
@ -36,38 +36,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
|||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "openUrlRequest":
|
||||
let id = arguments!["id"] as! String
|
||||
let urlRequest = arguments!["urlRequest"] as! [String:Any?]
|
||||
let options = arguments!["options"] as! [String: Any?]
|
||||
let contextMenu = arguments!["contextMenu"] as! [String: Any]
|
||||
let windowId = arguments!["windowId"] as? Int64
|
||||
let initialUserScripts = arguments!["initialUserScripts"] as? [[String: Any]]
|
||||
openUrlRequest(id: id, urlRequest: urlRequest, options: options, contextMenu: contextMenu, windowId: windowId, initialUserScripts: initialUserScripts)
|
||||
result(true)
|
||||
break
|
||||
case "openFile":
|
||||
let id = arguments!["id"] as! String
|
||||
let assetFilePath = arguments!["assetFilePath"] as! String
|
||||
let options = arguments!["options"] as! [String: Any?]
|
||||
let contextMenu = arguments!["contextMenu"] as! [String: Any]
|
||||
let windowId = arguments!["windowId"] as? Int64
|
||||
let initialUserScripts = arguments!["initialUserScripts"] as? [[String: Any]]
|
||||
openFile(id: id, assetFilePath: assetFilePath, options: options, contextMenu: contextMenu, windowId: windowId, initialUserScripts: initialUserScripts)
|
||||
result(true)
|
||||
break
|
||||
case "openData":
|
||||
let id = arguments!["id"] as! String
|
||||
let options = arguments!["options"] as! [String: Any?]
|
||||
let data = arguments!["data"] as! String
|
||||
let mimeType = arguments!["mimeType"] as! String
|
||||
let encoding = arguments!["encoding"] as! String
|
||||
let baseUrl = arguments!["baseUrl"] as! String
|
||||
let contextMenu = arguments!["contextMenu"] as! [String: Any]
|
||||
let windowId = arguments!["windowId"] as? Int64
|
||||
let initialUserScripts = arguments!["initialUserScripts"] as? [[String: Any]]
|
||||
openData(id: id, options: options, data: data, mimeType: mimeType, encoding: encoding, baseUrl: baseUrl,
|
||||
contextMenu: contextMenu, windowId: windowId, initialUserScripts: initialUserScripts)
|
||||
case "open":
|
||||
open(arguments: arguments!)
|
||||
result(true)
|
||||
break
|
||||
case "openWithSystemBrowser":
|
||||
|
@ -98,37 +68,25 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
|||
return webViewController
|
||||
}
|
||||
|
||||
public func openUrlRequest(id: String, urlRequest: [String:Any?], options: [String: Any?],
|
||||
contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
|
||||
let webViewController = prepareInAppBrowserWebViewController(options: options)
|
||||
|
||||
webViewController.id = id
|
||||
webViewController.initialUrlRequest = URLRequest.init(fromPluginMap: urlRequest)
|
||||
webViewController.contextMenu = contextMenu
|
||||
webViewController.windowId = windowId
|
||||
webViewController.initialUserScripts = initialUserScripts ?? []
|
||||
|
||||
presentViewController(webViewController: webViewController)
|
||||
}
|
||||
|
||||
public func openFile(id: String, assetFilePath: String, options: [String: Any?],
|
||||
contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
|
||||
public func open(arguments: NSDictionary) {
|
||||
let id = arguments["id"] as! String
|
||||
let urlRequest = arguments["urlRequest"] as? [String:Any?]
|
||||
let assetFilePath = arguments["assetFilePath"] as? String
|
||||
let data = arguments["data"] as? String
|
||||
let mimeType = arguments["mimeType"] as? String
|
||||
let encoding = arguments["encoding"] as? String
|
||||
let baseUrl = arguments["baseUrl"] as? String
|
||||
let options = arguments["options"] as! [String: Any?]
|
||||
let contextMenu = arguments["contextMenu"] as! [String: Any]
|
||||
let windowId = arguments["windowId"] as? Int64
|
||||
let initialUserScripts = arguments["initialUserScripts"] as? [[String: Any]]
|
||||
let pullToRefreshInitialOptions = arguments["pullToRefreshOptions"] as! [String: Any?]
|
||||
|
||||
let webViewController = prepareInAppBrowserWebViewController(options: options)
|
||||
|
||||
webViewController.id = id
|
||||
webViewController.initialUrlRequest = urlRequest != nil ? URLRequest.init(fromPluginMap: urlRequest!) : nil
|
||||
webViewController.initialFile = assetFilePath
|
||||
webViewController.contextMenu = contextMenu
|
||||
webViewController.windowId = windowId
|
||||
webViewController.initialUserScripts = initialUserScripts ?? []
|
||||
|
||||
presentViewController(webViewController: webViewController)
|
||||
}
|
||||
|
||||
public func openData(id: String, options: [String: Any?], data: String, mimeType: String, encoding: String,
|
||||
baseUrl: String, contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
|
||||
let webViewController = prepareInAppBrowserWebViewController(options: options)
|
||||
|
||||
webViewController.id = id
|
||||
webViewController.initialData = data
|
||||
webViewController.initialMimeType = mimeType
|
||||
webViewController.initialEncoding = encoding
|
||||
|
@ -136,6 +94,7 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
|||
webViewController.contextMenu = contextMenu
|
||||
webViewController.windowId = windowId
|
||||
webViewController.initialUserScripts = initialUserScripts ?? []
|
||||
webViewController.pullToRefreshInitialOptions = pullToRefreshInitialOptions
|
||||
|
||||
presentViewController(webViewController: webViewController)
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
|||
var initialBaseUrl: String?
|
||||
var previousStatusBarStyle = -1
|
||||
var initialUserScripts: [[String: Any]] = []
|
||||
var pullToRefreshInitialOptions: [String: Any?] = [:]
|
||||
var methodCallDelegate: InAppWebViewMethodHandler?
|
||||
|
||||
public override func loadView() {
|
||||
|
@ -64,6 +65,15 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
|||
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
|
||||
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
|
||||
|
||||
let pullToRefreshLayoutChannel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + id,
|
||||
binaryMessenger: SwiftFlutterPlugin.instance!.registrar!.messenger())
|
||||
let pullToRefreshOptions = PullToRefreshOptions()
|
||||
let _ = pullToRefreshOptions.parse(options: pullToRefreshInitialOptions)
|
||||
let pullToRefreshControl = PullToRefreshControl(channel: pullToRefreshLayoutChannel, options: pullToRefreshOptions)
|
||||
webView.pullToRefreshControl = pullToRefreshControl
|
||||
pullToRefreshControl.delegate = webView
|
||||
pullToRefreshControl.prepare()
|
||||
|
||||
prepareWebView()
|
||||
|
||||
progressBar = UIProgressView(progressViewStyle: .bar)
|
||||
|
|
|
@ -23,13 +23,8 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
|||
self.registrar = registrar
|
||||
self.viewId = viewId
|
||||
|
||||
var channelName = ""
|
||||
if let id = viewId as? Int64 {
|
||||
channelName = "com.pichillilorenzo/flutter_inappwebview_" + String(id)
|
||||
} else if let id = viewId as? String {
|
||||
channelName = "com.pichillilorenzo/flutter_inappwebview_" + id
|
||||
}
|
||||
channel = FlutterMethodChannel(name: channelName, binaryMessenger: registrar.messenger())
|
||||
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_" + String(describing: viewId),
|
||||
binaryMessenger: registrar.messenger())
|
||||
|
||||
myView = UIView(frame: frame)
|
||||
myView!.clipsToBounds = true
|
||||
|
@ -41,6 +36,7 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
|||
let contextMenu = args["contextMenu"] as? [String: Any]
|
||||
let windowId = args["windowId"] as? Int64
|
||||
let initialUserScripts = args["initialUserScripts"] as? [[String: Any]]
|
||||
let pullToRefreshInitialOptions = args["pullToRefreshOptions"] as! [String: Any?]
|
||||
|
||||
var userScripts: [UserScript] = []
|
||||
if let initialUserScripts = initialUserScripts {
|
||||
|
@ -70,6 +66,15 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
|||
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
|
||||
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
|
||||
|
||||
let pullToRefreshLayoutChannel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_" + String(describing: viewId),
|
||||
binaryMessenger: registrar.messenger())
|
||||
let pullToRefreshOptions = PullToRefreshOptions()
|
||||
let _ = pullToRefreshOptions.parse(options: pullToRefreshInitialOptions)
|
||||
let pullToRefreshControl = PullToRefreshControl(channel: pullToRefreshLayoutChannel, options: pullToRefreshOptions)
|
||||
webView!.pullToRefreshControl = pullToRefreshControl
|
||||
pullToRefreshControl.delegate = webView!
|
||||
pullToRefreshControl.prepare()
|
||||
|
||||
webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
myView!.autoresizesSubviews = true
|
||||
myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||
|
|
|
@ -9,16 +9,20 @@ import Flutter
|
|||
import Foundation
|
||||
import WebKit
|
||||
|
||||
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate {
|
||||
public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler, UIGestureRecognizerDelegate, PullToRefreshDelegate {
|
||||
|
||||
var windowId: Int64?
|
||||
var inAppBrowserDelegate: InAppBrowserDelegate?
|
||||
var channel: FlutterMethodChannel?
|
||||
var options: InAppWebViewOptions?
|
||||
var pullToRefreshControl: PullToRefreshControl?
|
||||
|
||||
static var sslCertificatesMap: [String: SslCertificate] = [:] // [URL host name : SslCertificate]
|
||||
static var credentialsProposed: [URLCredential] = []
|
||||
|
||||
var lastScrollX: CGFloat = 0
|
||||
var lastScrollY: CGFloat = 0
|
||||
|
||||
var isPausedTimers = false
|
||||
var isPausedTimersCompletionHandler: (() -> Void)?
|
||||
|
||||
|
@ -251,9 +255,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
return super.canPerformAction(action, withSender: sender)
|
||||
}
|
||||
|
||||
public func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
|
||||
// fix for pull-to-refresh jittering when the touch drag event is held
|
||||
if let pullToRefreshControl = pullToRefreshControl,
|
||||
pullToRefreshControl.shouldCallOnRefresh {
|
||||
pullToRefreshControl.onRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
public func prepare() {
|
||||
self.scrollView.addGestureRecognizer(self.longPressRecognizer)
|
||||
self.scrollView.addGestureRecognizer(self.recognizerForDisablingContextMenuOnLinks)
|
||||
scrollView.addGestureRecognizer(self.longPressRecognizer)
|
||||
scrollView.addGestureRecognizer(self.recognizerForDisablingContextMenuOnLinks)
|
||||
scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset), options: [.new, .old], context: nil)
|
||||
|
||||
addObserver(self,
|
||||
forKeyPath: #keyPath(WKWebView.estimatedProgress),
|
||||
|
@ -276,7 +289,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
name: UIMenuController.willShowMenuNotification,
|
||||
object: nil)
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(onHideContextMenu),
|
||||
|
@ -566,7 +578,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
let newTitle = change?[NSKeyValueChangeKey.newKey] as? String
|
||||
onTitleChanged(title: newTitle)
|
||||
inAppBrowserDelegate?.didChangeTitle(title: newTitle)
|
||||
}
|
||||
} else if keyPath == #keyPath(UIScrollView.contentOffset) {
|
||||
let newContentOffset = change?[NSKeyValueChangeKey.newKey] as? CGPoint
|
||||
let oldContentOffset = change?[NSKeyValueChangeKey.oldKey] as? CGPoint
|
||||
if scrollView.isDragging || scrollView.isDecelerating || newContentOffset != oldContentOffset {
|
||||
onScrollChanged()
|
||||
}
|
||||
}
|
||||
replaceGestureHandlerIfNeeded()
|
||||
}
|
||||
|
||||
|
@ -1956,7 +1974,15 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
|||
})
|
||||
}
|
||||
|
||||
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||
/// UIScrollViewDelegate is somehow bugged:
|
||||
/// if InAppWebView implements the UIScrollViewDelegate protocol and implement the scrollViewDidScroll event,
|
||||
/// then, when the user scrolls the content, the webview content is not rendered (just white space).
|
||||
/// Calling setNeedsLayout() resolves this problem, but, for some reason, the bounce effect is canceled.
|
||||
///
|
||||
/// So, to track the same event, without implementing the scrollViewDidScroll event, we create
|
||||
/// an observer that observes the scrollView.contentOffset property.
|
||||
/// This way, we don't need to call setNeedsLayout() and all works fine.
|
||||
public func onScrollChanged() {
|
||||
let disableVerticalScroll = options?.disableVerticalScroll ?? false
|
||||
let disableHorizontalScroll = options?.disableHorizontalScroll ?? false
|
||||
if disableVerticalScroll && disableHorizontalScroll {
|
||||
|
@ -2635,6 +2661,23 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
}
|
||||
}
|
||||
|
||||
public func enablePullToRefresh() {
|
||||
if let pullToRefreshControl = pullToRefreshControl {
|
||||
if #available(iOS 10.0, *) {
|
||||
scrollView.refreshControl = pullToRefreshControl
|
||||
} else {
|
||||
scrollView.addSubview(pullToRefreshControl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func disablePullToRefresh() {
|
||||
pullToRefreshControl?.removeFromSuperview()
|
||||
if #available(iOS 10.0, *) {
|
||||
scrollView.refreshControl = nil
|
||||
}
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
if isPausedTimers, let completionHandler = isPausedTimersCompletionHandler {
|
||||
isPausedTimersCompletionHandler = nil
|
||||
|
@ -2659,12 +2702,16 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
|||
for imp in customIMPs {
|
||||
imp_removeBlock(imp)
|
||||
}
|
||||
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
|
||||
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
|
||||
longPressRecognizer.delegate = nil
|
||||
scrollView.removeGestureRecognizer(longPressRecognizer)
|
||||
recognizerForDisablingContextMenuOnLinks.removeTarget(self, action: #selector(longPressGestureDetected))
|
||||
recognizerForDisablingContextMenuOnLinks.delegate = nil
|
||||
scrollView.removeGestureRecognizer(recognizerForDisablingContextMenuOnLinks)
|
||||
disablePullToRefresh()
|
||||
pullToRefreshControl?.dispose()
|
||||
pullToRefreshControl = nil
|
||||
uiDelegate = nil
|
||||
navigationDelegate = nil
|
||||
scrollView.delegate = nil
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import Foundation
|
||||
import WebKit
|
||||
|
||||
class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
||||
public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
||||
var webView: InAppWebView?
|
||||
|
||||
init(webView: InAppWebView) {
|
||||
|
|
|
@ -11,8 +11,8 @@ public class LeakAvoider: NSObject {
|
|||
weak var delegate : FlutterMethodCallDelegate?
|
||||
|
||||
init(delegate: FlutterMethodCallDelegate) {
|
||||
self.delegate = delegate
|
||||
super.init()
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
//
|
||||
// File.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 03/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Flutter
|
||||
|
||||
public class PullToRefreshControl : UIRefreshControl, FlutterPlugin {
|
||||
|
||||
var channel: FlutterMethodChannel?
|
||||
var options: PullToRefreshOptions?
|
||||
var shouldCallOnRefresh = false
|
||||
var delegate: PullToRefreshDelegate?
|
||||
|
||||
public init(channel: FlutterMethodChannel?, options: PullToRefreshOptions?) {
|
||||
super.init()
|
||||
self.channel = channel
|
||||
self.options = options
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
}
|
||||
|
||||
public static func register(with registrar: FlutterPluginRegistrar) {
|
||||
|
||||
}
|
||||
|
||||
public func prepare() {
|
||||
self.channel?.setMethodCallHandler(self.handle)
|
||||
if let options = options {
|
||||
if options.enabled {
|
||||
delegate?.enablePullToRefresh()
|
||||
}
|
||||
if let color = options.color, !color.isEmpty {
|
||||
tintColor = UIColor(hexString: color)
|
||||
}
|
||||
if let backgroundTintColor = options.backgroundColor, !backgroundTintColor.isEmpty {
|
||||
backgroundColor = UIColor(hexString: backgroundTintColor)
|
||||
}
|
||||
if let attributedTitleMap = options.attributedTitle {
|
||||
attributedTitle = NSAttributedString.fromMap(map: attributedTitleMap)
|
||||
}
|
||||
}
|
||||
addTarget(self, action: #selector(updateShouldCallOnRefresh), for: .valueChanged)
|
||||
}
|
||||
|
||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
||||
let arguments = call.arguments as? NSDictionary
|
||||
|
||||
switch call.method {
|
||||
case "setEnabled":
|
||||
let enabled = arguments!["enabled"] as! Bool
|
||||
if enabled {
|
||||
delegate?.enablePullToRefresh()
|
||||
} else {
|
||||
delegate?.disablePullToRefresh()
|
||||
}
|
||||
result(true)
|
||||
break
|
||||
case "setRefreshing":
|
||||
let refreshing = arguments!["refreshing"] as! Bool
|
||||
if refreshing {
|
||||
self.beginRefreshing()
|
||||
} else {
|
||||
self.endRefreshing()
|
||||
}
|
||||
result(true)
|
||||
break
|
||||
case "setColor":
|
||||
let color = arguments!["color"] as! String
|
||||
tintColor = UIColor(hexString: color)
|
||||
result(true)
|
||||
break
|
||||
case "setBackgroundColor":
|
||||
let color = arguments!["color"] as! String
|
||||
backgroundColor = UIColor(hexString: color)
|
||||
result(true)
|
||||
break
|
||||
case "setAttributedTitle":
|
||||
let attributedTitleMap = arguments!["attributedTitle"] as! [String: Any?]
|
||||
attributedTitle = NSAttributedString.fromMap(map: attributedTitleMap)
|
||||
result(true)
|
||||
break
|
||||
default:
|
||||
result(FlutterMethodNotImplemented)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func onRefresh() {
|
||||
shouldCallOnRefresh = false
|
||||
let arguments: [String: Any?] = [:]
|
||||
self.channel?.invokeMethod("onRefresh", arguments: arguments)
|
||||
}
|
||||
|
||||
@objc public func updateShouldCallOnRefresh() {
|
||||
shouldCallOnRefresh = true
|
||||
}
|
||||
|
||||
public func dispose() {
|
||||
channel?.setMethodCallHandler(nil)
|
||||
removeTarget(self, action: #selector(updateShouldCallOnRefresh), for: .valueChanged)
|
||||
delegate = nil
|
||||
}
|
||||
|
||||
deinit {
|
||||
print("PullToRefreshControl - dealloc")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
//
|
||||
// PullToRefreshDelegate.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 04/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public protocol PullToRefreshDelegate {
|
||||
func enablePullToRefresh()
|
||||
func disablePullToRefresh()
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// PullToRefreshOptions.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 03/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class PullToRefreshOptions : Options<PullToRefreshControl> {
|
||||
|
||||
var enabled = true
|
||||
var color: String?
|
||||
var backgroundColor: String?
|
||||
var attributedTitle: [String: Any?]?
|
||||
|
||||
override init(){
|
||||
super.init()
|
||||
}
|
||||
|
||||
override func parse(options: [String: Any?]) -> PullToRefreshOptions {
|
||||
let _ = super.parse(options: options)
|
||||
if let attributedTitle = options["attributedTitle"] as? [String: Any?] {
|
||||
self.attributedTitle = attributedTitle
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
override func getRealOptions(obj: PullToRefreshControl?) -> [String: Any?] {
|
||||
let realOptions: [String: Any?] = toMap()
|
||||
return realOptions
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//
|
||||
// NSAttributedString.swift
|
||||
// flutter_inappwebview
|
||||
//
|
||||
// Created by Lorenzo Pichilli on 05/03/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension NSAttributedString {
|
||||
public static func fromMap(map: [String:Any?]?) -> NSAttributedString? {
|
||||
guard let map = map, let string = map["string"] as? String else {
|
||||
return nil
|
||||
}
|
||||
|
||||
var attributes: [NSAttributedString.Key : Any] = [:]
|
||||
|
||||
if let backgroundColor = map["backgroundColor"] as? String {
|
||||
attributes[.backgroundColor] = UIColor(hexString: backgroundColor)
|
||||
}
|
||||
if let baselineOffset = map["baselineOffset"] as? Double {
|
||||
attributes[.baselineOffset] = baselineOffset
|
||||
}
|
||||
if let expansion = map["expansion"] as? Double {
|
||||
attributes[.expansion] = expansion
|
||||
}
|
||||
if let foregroundColor = map["foregroundColor"] as? String {
|
||||
attributes[.foregroundColor] = UIColor(hexString: foregroundColor)
|
||||
}
|
||||
if let kern = map["kern"] as? Double {
|
||||
attributes[.kern] = kern
|
||||
}
|
||||
if let ligature = map["ligature"] as? Int64 {
|
||||
attributes[.ligature] = ligature
|
||||
}
|
||||
if let obliqueness = map["obliqueness"] as? Double {
|
||||
attributes[.obliqueness] = obliqueness
|
||||
}
|
||||
if let strikethroughColor = map["strikethroughColor"] as? String {
|
||||
attributes[.strikethroughColor] = UIColor(hexString: strikethroughColor)
|
||||
}
|
||||
if let strikethroughStyle = map["strikethroughStyle"] as? Int64 {
|
||||
attributes[.strikethroughStyle] = strikethroughStyle
|
||||
}
|
||||
if let strokeColor = map["strokeColor"] as? String {
|
||||
attributes[.strokeColor] = UIColor(hexString: strokeColor)
|
||||
}
|
||||
if let strokeWidth = map["strokeWidth"] as? Double {
|
||||
attributes[.strokeWidth] = strokeWidth
|
||||
}
|
||||
if let textEffect = map["textEffect"] as? String {
|
||||
attributes[.textEffect] = textEffect
|
||||
}
|
||||
if let underlineColor = map["underlineColor"] as? String {
|
||||
attributes[.underlineColor] = UIColor(hexString: underlineColor)
|
||||
}
|
||||
if let underlineStyle = map["underlineStyle"] as? Int64 {
|
||||
attributes[.underlineStyle] = underlineStyle
|
||||
}
|
||||
|
||||
return NSAttributedString(string: string, attributes: attributes)
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ import 'dart:collection';
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
import 'package:flutter_inappwebview/src/util.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
|
@ -40,14 +41,17 @@ class InAppBrowserNotOpenedException implements Exception {
|
|||
///This class uses the native WebView of the platform.
|
||||
///The [webViewController] field can be used to access the [InAppWebViewController] API.
|
||||
class InAppBrowser {
|
||||
///Browser's UUID.
|
||||
late String id;
|
||||
///View ID.
|
||||
late final String id;
|
||||
|
||||
///Context menu used by the browser. It should be set before opening the browser.
|
||||
ContextMenu? contextMenu;
|
||||
|
||||
///Represents the pull-to-refresh feature controller.
|
||||
PullToRefreshController? pullToRefreshController;
|
||||
|
||||
///Initial list of user scripts to be loaded at start or end of a page loading.
|
||||
UnmodifiableListView<UserScript>? initialUserScripts;
|
||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||
|
||||
bool _isOpened = false;
|
||||
late MethodChannel _channel;
|
||||
|
@ -68,13 +72,14 @@ class InAppBrowser {
|
|||
this._channel.setMethodCallHandler(handleMethod);
|
||||
_isOpened = false;
|
||||
webViewController = new InAppWebViewController.fromInAppBrowser(
|
||||
id, this._channel, this, this.initialUserScripts);
|
||||
this._channel, this, this.initialUserScripts);
|
||||
}
|
||||
|
||||
Future<dynamic> handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onBrowserCreated":
|
||||
this._isOpened = true;
|
||||
this.pullToRefreshController?.initMethodChannel(id);
|
||||
onBrowserCreated();
|
||||
break;
|
||||
case "onExit":
|
||||
|
@ -106,7 +111,8 @@ class InAppBrowser {
|
|||
args.putIfAbsent('windowId', () => windowId);
|
||||
args.putIfAbsent('initialUserScripts',
|
||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
||||
await _sharedChannel.invokeMethod('openUrlRequest', args);
|
||||
args.putIfAbsent('pullToRefreshOptions', () => pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap());
|
||||
await _sharedChannel.invokeMethod('open', args);
|
||||
}
|
||||
|
||||
///Opens the given [assetFilePath] file in a new [InAppBrowser] instance.
|
||||
|
@ -159,7 +165,8 @@ class InAppBrowser {
|
|||
args.putIfAbsent('windowId', () => windowId);
|
||||
args.putIfAbsent('initialUserScripts',
|
||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
||||
await _sharedChannel.invokeMethod('openFile', args);
|
||||
args.putIfAbsent('pullToRefreshOptions', () => pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap());
|
||||
await _sharedChannel.invokeMethod('open', args);
|
||||
}
|
||||
|
||||
///Opens a new [InAppBrowser] instance with [data] as a content, using [baseUrl] as the base URL for it.
|
||||
|
@ -194,7 +201,8 @@ class InAppBrowser {
|
|||
args.putIfAbsent('windowId', () => windowId);
|
||||
args.putIfAbsent('initialUserScripts',
|
||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
||||
await _sharedChannel.invokeMethod('openData', args);
|
||||
args.putIfAbsent('pullToRefreshOptions', () => pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap());
|
||||
await _sharedChannel.invokeMethod('open', args);
|
||||
}
|
||||
|
||||
///This is a static method that opens an [url] in the system browser. You wont be able to use the [InAppBrowser] methods here!
|
||||
|
|
|
@ -9,13 +9,17 @@ import '../types.dart';
|
|||
import 'webview.dart';
|
||||
import 'in_app_webview_controller.dart';
|
||||
import 'in_app_webview_options.dart';
|
||||
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||
import '../pull_to_refresh/pull_to_refresh_options.dart';
|
||||
|
||||
///Class that represents a WebView in headless mode.
|
||||
///It can be used to run a WebView in background without attaching an `InAppWebView` to the widget tree.
|
||||
///
|
||||
///Remember to dispose it when you don't need it anymore.
|
||||
class HeadlessInAppWebView implements WebView {
|
||||
late String id;
|
||||
///View ID.
|
||||
late final String id;
|
||||
|
||||
bool _isDisposed = true;
|
||||
static const MethodChannel _sharedChannel =
|
||||
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
|
||||
|
@ -85,7 +89,8 @@ class HeadlessInAppWebView implements WebView {
|
|||
this.initialData,
|
||||
this.initialOptions,
|
||||
this.contextMenu,
|
||||
this.initialUserScripts}) {
|
||||
this.initialUserScripts,
|
||||
this.pullToRefreshController}) {
|
||||
id = ViewIdGenerator.generateId();
|
||||
webViewController = new InAppWebViewController(id, this);
|
||||
}
|
||||
|
@ -93,6 +98,7 @@ class HeadlessInAppWebView implements WebView {
|
|||
Future<dynamic> handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onHeadlessWebViewCreated":
|
||||
pullToRefreshController?.initMethodChannel(id);
|
||||
if (onWebViewCreated != null) {
|
||||
onWebViewCreated!(webViewController);
|
||||
}
|
||||
|
@ -123,6 +129,7 @@ class HeadlessInAppWebView implements WebView {
|
|||
'windowId': this.windowId,
|
||||
'initialUserScripts':
|
||||
this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||
'pullToRefreshOptions': this.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||
});
|
||||
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
|
||||
}
|
||||
|
@ -177,6 +184,9 @@ class HeadlessInAppWebView implements WebView {
|
|||
@override
|
||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||
|
||||
@override
|
||||
final PullToRefreshController? pullToRefreshController;
|
||||
|
||||
@override
|
||||
final void Function(InAppWebViewController controller, Uri? url)?
|
||||
onPageCommitVisible;
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
import '../types.dart';
|
||||
|
@ -15,6 +16,7 @@ import '../types.dart';
|
|||
import 'webview.dart';
|
||||
import 'in_app_webview_controller.dart';
|
||||
import 'in_app_webview_options.dart';
|
||||
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||
|
||||
///Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree.
|
||||
class InAppWebView extends StatefulWidget implements WebView {
|
||||
|
@ -38,6 +40,7 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
this.initialData,
|
||||
this.initialOptions,
|
||||
this.initialUserScripts,
|
||||
this.pullToRefreshController,
|
||||
this.contextMenu,
|
||||
this.onWebViewCreated,
|
||||
this.onLoadStart,
|
||||
|
@ -133,6 +136,9 @@ class InAppWebView extends StatefulWidget implements WebView {
|
|||
@override
|
||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||
|
||||
@override
|
||||
final PullToRefreshController? pullToRefreshController;
|
||||
|
||||
@override
|
||||
final ContextMenu? contextMenu;
|
||||
|
||||
|
@ -379,6 +385,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
'initialUserScripts':
|
||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ??
|
||||
[],
|
||||
'pullToRefreshOptions': widget.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||
},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
)
|
||||
|
@ -405,6 +412,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
'windowId': widget.windowId,
|
||||
'initialUserScripts':
|
||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||
'pullToRefreshOptions': widget.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||
},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
|
@ -425,6 +433,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
'windowId': widget.windowId,
|
||||
'initialUserScripts':
|
||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||
'pullToRefreshOptions': widget.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||
},
|
||||
creationParamsCodec: const StandardMessageCodec(),
|
||||
);
|
||||
|
@ -445,6 +454,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
|||
|
||||
void _onPlatformViewCreated(int id) {
|
||||
_controller = InAppWebViewController(id, widget);
|
||||
widget.pullToRefreshController?.initMethodChannel(id);
|
||||
if (widget.onWebViewCreated != null) {
|
||||
widget.onWebViewCreated!(_controller);
|
||||
}
|
||||
|
|
|
@ -52,15 +52,9 @@ class InAppWebViewController {
|
|||
HashMap<String, JavaScriptHandlerCallback>();
|
||||
List<UserScript> _userScripts = [];
|
||||
|
||||
// ignore: unused_field
|
||||
bool _isOpened = false;
|
||||
|
||||
// ignore: unused_field
|
||||
dynamic _id;
|
||||
|
||||
// ignore: unused_field
|
||||
String? _inAppBrowserUuid;
|
||||
|
||||
InAppBrowser? _inAppBrowser;
|
||||
|
||||
///Android controller that contains only android-specific methods
|
||||
|
@ -84,11 +78,9 @@ class InAppWebViewController {
|
|||
}
|
||||
|
||||
InAppWebViewController.fromInAppBrowser(
|
||||
String uuid,
|
||||
MethodChannel channel,
|
||||
InAppBrowser inAppBrowser,
|
||||
UnmodifiableListView<UserScript>? initialUserScripts) {
|
||||
this._inAppBrowserUuid = uuid;
|
||||
this._channel = channel;
|
||||
this._inAppBrowser = inAppBrowser;
|
||||
this._userScripts =
|
||||
|
|
|
@ -157,7 +157,7 @@ class IOSInAppWebViewOptions
|
|||
///Set to `true` to enable Apple Pay API for the [WebView] at its first page load or on the next page load (using [InAppWebViewController.setOptions]). The default value is `false`.
|
||||
///
|
||||
///**IMPORTANT NOTE**: As written in the official [Safari 13 Release Notes](https://developer.apple.com/documentation/safari-release-notes/safari-13-release-notes#Payment-Request-API),
|
||||
///it won't work if any script injection APIs is used (such as [InAppWebViewController.evaluateJavascript] or [UserScript]).
|
||||
///it won't work if any script injection APIs are used (such as [InAppWebViewController.evaluateJavascript] or [UserScript]).
|
||||
///So, when this attribute is `true`, all the methods, options, and events implemented using JavaScript won't be called or won't do anything and the result will always be `null`.
|
||||
///
|
||||
///Methods affected:
|
||||
|
|
|
@ -3,5 +3,6 @@ export 'in_app_webview.dart';
|
|||
export 'in_app_webview_controller.dart';
|
||||
export 'in_app_webview_options.dart';
|
||||
export 'headless_in_app_webview.dart';
|
||||
export '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||
export 'android/main.dart';
|
||||
export 'ios/main.dart';
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||
|
||||
import '../context_menu.dart';
|
||||
import '../types.dart';
|
||||
|
||||
|
@ -642,6 +644,9 @@ abstract class WebView {
|
|||
///This is a limitation of the native iOS WebKit APIs.
|
||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||
|
||||
///Represents the pull-to-refresh feature controller.
|
||||
final PullToRefreshController? pullToRefreshController;
|
||||
|
||||
WebView(
|
||||
{this.windowId,
|
||||
this.onWebViewCreated,
|
||||
|
@ -701,5 +706,6 @@ abstract class WebView {
|
|||
this.initialData,
|
||||
this.initialOptions,
|
||||
this.contextMenu,
|
||||
this.initialUserScripts});
|
||||
this.initialUserScripts,
|
||||
this.pullToRefreshController});
|
||||
}
|
||||
|
|
|
@ -14,3 +14,4 @@ export 'in_app_localhost_server.dart';
|
|||
export 'content_blocker.dart';
|
||||
export 'http_auth_credentials_database.dart';
|
||||
export 'context_menu.dart';
|
||||
export 'pull_to_refresh/main.dart';
|
|
@ -0,0 +1,2 @@
|
|||
export 'pull_to_refresh_controller.dart';
|
||||
export 'pull_to_refresh_options.dart';
|
|
@ -0,0 +1,130 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import '../in_app_webview/webview.dart';
|
||||
import '../in_app_browser/in_app_browser.dart';
|
||||
import '../util.dart';
|
||||
import '../types.dart';
|
||||
import 'pull_to_refresh_options.dart';
|
||||
|
||||
///A standard controller that can initiate the refreshing of a scroll view’s contents.
|
||||
///This should be used whenever the user can refresh the contents of a WebView via a vertical swipe gesture.
|
||||
///
|
||||
///All the methods should be called only when the WebView has been created or is already running
|
||||
///(for example [WebView.onWebViewCreated] or [InAppBrowser.onBrowserCreated]).
|
||||
class PullToRefreshController {
|
||||
late PullToRefreshOptions options;
|
||||
MethodChannel? _channel;
|
||||
|
||||
///Event called when a swipe gesture triggers a refresh.
|
||||
final void Function()? onRefresh;
|
||||
|
||||
PullToRefreshController({PullToRefreshOptions? options, this.onRefresh}) {
|
||||
this.options = options ?? PullToRefreshOptions();
|
||||
}
|
||||
|
||||
Future<dynamic> handleMethod(MethodCall call) async {
|
||||
switch (call.method) {
|
||||
case "onRefresh":
|
||||
if (onRefresh != null)
|
||||
onRefresh!();
|
||||
break;
|
||||
default:
|
||||
throw UnimplementedError("Unimplemented ${call.method} method");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
///Sets whether the pull-to-refresh feature is enabled or not.
|
||||
Future<void> setEnabled(bool enabled) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('enabled', () => enabled);
|
||||
await _channel?.invokeMethod('setEnabled', args);
|
||||
}
|
||||
|
||||
Future<void> _setRefreshing(bool refreshing) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('refreshing', () => refreshing);
|
||||
await _channel?.invokeMethod('setRefreshing', args);
|
||||
}
|
||||
|
||||
///Tells the controller that a refresh operation was started programmatically.
|
||||
///
|
||||
///Call this method when an external event source triggers a programmatic refresh of your scrolling view.
|
||||
///This method updates the state of the refresh control to reflect the in-progress refresh operation.
|
||||
///When the refresh operation ends, be sure to call the [endRefreshing] method to return the controller to its default state.
|
||||
Future<void> beginRefreshing() async {
|
||||
return await _setRefreshing(true);
|
||||
}
|
||||
|
||||
///Tells the controller that a refresh operation has ended.
|
||||
///
|
||||
///Call this method at the end of any refresh operation (whether it was initiated programmatically or by the user)
|
||||
///to return the refresh control to its default state.
|
||||
///If the refresh control is at least partially visible, calling this method also hides it.
|
||||
///If animations are also enabled, the control is hidden using an animation.
|
||||
Future<void> endRefreshing() async {
|
||||
await _setRefreshing(false);
|
||||
}
|
||||
|
||||
///Returns whether a refresh operation has been triggered and is in progress.
|
||||
Future<bool> isRefreshing() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await _channel?.invokeMethod('isRefreshing', args);
|
||||
}
|
||||
|
||||
///Sets the color of the refresh control.
|
||||
Future<void> setColor(Color color) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('color', () => color.toHex());
|
||||
await _channel?.invokeMethod('setColor', args);
|
||||
}
|
||||
|
||||
///Sets the background color of the refresh control.
|
||||
Future<void> setBackgroundColor(Color color) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('color', () => color.toHex());
|
||||
await _channel?.invokeMethod('setBackgroundColor', args);
|
||||
}
|
||||
|
||||
///Set the distance to trigger a sync in dips.
|
||||
///
|
||||
///**NOTE**: Available only on Android.
|
||||
Future<void> setDistanceToTriggerSync(int distanceToTriggerSync) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('distanceToTriggerSync', () => distanceToTriggerSync);
|
||||
await _channel?.invokeMethod('setDistanceToTriggerSync', args);
|
||||
}
|
||||
|
||||
///Sets the distance that the refresh indicator can be pulled beyond its resting position during a swipe gesture.
|
||||
///
|
||||
///**NOTE**: Available only on Android.
|
||||
Future<void> setSlingshotDistance(int slingshotDistance) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('slingshotDistance', () => slingshotDistance);
|
||||
await _channel?.invokeMethod('setSlingshotDistance', args);
|
||||
}
|
||||
|
||||
///Gets the default distance that the refresh indicator can be pulled beyond its resting position during a swipe gesture.
|
||||
///
|
||||
///**NOTE**: Available only on Android.
|
||||
Future<int> getDefaultSlingshotDistance() async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
return await _channel?.invokeMethod('getDefaultSlingshotDistance', args);
|
||||
}
|
||||
|
||||
///Sets the styled title text to display in the refresh control.
|
||||
///
|
||||
///**NOTE**: Available only on iOS.
|
||||
Future<void> setAttributedTitle(IOSNSAttributedString attributedTitle) async {
|
||||
Map<String, dynamic> args = <String, dynamic>{};
|
||||
args.putIfAbsent('attributedTitle', () => attributedTitle.toMap());
|
||||
await _channel?.invokeMethod('setAttributedTitle', args);
|
||||
}
|
||||
|
||||
void initMethodChannel(dynamic id) {
|
||||
this._channel =
|
||||
MethodChannel('com.pichillilorenzo/flutter_inappwebview_pull_to_refresh_$id');
|
||||
this._channel?.setMethodCallHandler(handleMethod);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
import 'dart:ui';
|
||||
import '../util.dart';
|
||||
import '../types.dart';
|
||||
|
||||
class PullToRefreshOptions {
|
||||
///Sets whether the pull-to-refresh feature is enabled or not.
|
||||
bool enabled;
|
||||
|
||||
///The color of the refresh control.
|
||||
Color? color;
|
||||
|
||||
///The background color of the refresh control.
|
||||
Color? backgroundColor;
|
||||
|
||||
///The distance to trigger a sync in dips.
|
||||
///
|
||||
///**NOTE**: Available only on Android.
|
||||
int? distanceToTriggerSync;
|
||||
|
||||
///The distance in pixels that the refresh indicator can be pulled beyond its resting position.
|
||||
///
|
||||
///**NOTE**: Available only on Android.
|
||||
int? slingshotDistance;
|
||||
|
||||
///The title text to display in the refresh control.
|
||||
///
|
||||
///**NOTE**: Available only on iOS.
|
||||
IOSNSAttributedString? attributedTitle;
|
||||
|
||||
PullToRefreshOptions({
|
||||
this.enabled = true,
|
||||
this.color,
|
||||
this.backgroundColor,
|
||||
this.distanceToTriggerSync,
|
||||
this.slingshotDistance,
|
||||
this.attributedTitle
|
||||
});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"enabled": enabled,
|
||||
"color": color?.toHex(),
|
||||
"backgroundColor": backgroundColor?.toHex(),
|
||||
"distanceToTriggerSync": distanceToTriggerSync,
|
||||
"slingshotDistance": slingshotDistance,
|
||||
"attributedTitle": attributedTitle?.toMap() ?? {}
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ import 'dart:collection';
|
|||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:convert';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
|
@ -13,6 +14,9 @@ import 'in_app_webview/in_app_webview_controller.dart';
|
|||
import 'http_auth_credentials_database.dart';
|
||||
import 'cookie_manager.dart';
|
||||
import 'web_storage/web_storage.dart';
|
||||
import 'pull_to_refresh/pull_to_refresh_controller.dart';
|
||||
import 'pull_to_refresh/pull_to_refresh_options.dart';
|
||||
import 'util.dart';
|
||||
|
||||
///This type represents a callback, added with [InAppWebViewController.addJavaScriptHandler], that listens to post messages sent from JavaScript.
|
||||
///
|
||||
|
@ -6495,3 +6499,289 @@ class IOSWKWindowFeatures {
|
|||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
///An iOS-specific class that represents a string with associated attributes
|
||||
///used by the [PullToRefreshController] and [PullToRefreshOptions] classes.
|
||||
class IOSNSAttributedString {
|
||||
///The characters for the new object.
|
||||
String string;
|
||||
|
||||
///The color of the background behind the text.
|
||||
///
|
||||
///The value of this attribute is a [Color] object.
|
||||
///Use this attribute to specify the color of the background area behind the text.
|
||||
///If you do not specify this attribute, no background color is drawn.
|
||||
Color? backgroundColor;
|
||||
|
||||
///The vertical offset for the position of the text.
|
||||
///
|
||||
///The value of this attribute is a number containing a floating point value indicating the character’s offset from the baseline, in points.
|
||||
///The default value is `0`.
|
||||
double? baselineOffset;
|
||||
|
||||
///The expansion factor of the text.
|
||||
///
|
||||
///The value of this attribute is a number containing a floating point value indicating the log of the expansion factor to be applied to glyphs.
|
||||
///The default value is `0`, indicating no expansion.
|
||||
double? expansion;
|
||||
|
||||
///The color of the text.
|
||||
///
|
||||
///The value of this attribute is a [Color] object.
|
||||
///Use this attribute to specify the color of the text during rendering.
|
||||
///If you do not specify this attribute, the text is rendered in black.
|
||||
Color? foregroundColor;
|
||||
|
||||
///The kerning of the text.
|
||||
///
|
||||
///The value of this attribute is a number containing a floating-point value.
|
||||
///This value specifies the number of points by which to adjust kern-pair characters.
|
||||
///Kerning prevents unwanted space from occurring between specific characters and depends on the font.
|
||||
///The value `0` means kerning is disabled. The default value for this attribute is `0`.
|
||||
double? kern;
|
||||
|
||||
///The ligature of the text.
|
||||
///
|
||||
///The value of this attribute is a number containing an integer.
|
||||
///Ligatures cause specific character combinations to be rendered using a single custom glyph that corresponds to those characters.
|
||||
///The value `0` indicates no ligatures. The value `1` indicates the use of the default ligatures.
|
||||
///The value `2` indicates the use of all ligatures.
|
||||
///The default value for this attribute is `1`. (Value `2` is unsupported on iOS.)
|
||||
int? ligature;
|
||||
|
||||
///The obliqueness of the text.
|
||||
///
|
||||
///The value of this attribute is a number containing a floating point value indicating skew to be applied to glyphs.
|
||||
///The default value is `0`, indicating no skew.
|
||||
double? obliqueness;
|
||||
|
||||
///The color of the strikethrough.
|
||||
///
|
||||
///The value of this attribute is a [Color] object. The default value is `null`, indicating same as foreground color.
|
||||
Color? strikethroughColor;
|
||||
|
||||
///The strikethrough style of the text.
|
||||
///
|
||||
///This value indicates whether the text has a line through it and corresponds to one of the constants described in [IOSNSUnderlineStyle].
|
||||
///The default value for this attribute is [IOSNSUnderlineStyle.STYLE_NONE].
|
||||
IOSNSUnderlineStyle? strikethroughStyle;
|
||||
|
||||
///The color of the stroke.
|
||||
///
|
||||
///The value of this parameter is a [Color] object.
|
||||
///If it is not defined (which is the case by default), it is assumed to be the same as the value of foregroundColor;
|
||||
///otherwise, it describes the outline color.
|
||||
Color? strokeColor;
|
||||
|
||||
///The width of the stroke.
|
||||
///
|
||||
///The value of this attribute is a number containing a floating-point value.
|
||||
///This value represents the amount to change the stroke width and is specified as a percentage of the font point size.
|
||||
///Specify `0` (the default) for no additional changes.
|
||||
///Specify positive values to change the stroke width alone.
|
||||
///Specify negative values to stroke and fill the text.
|
||||
///For example, a typical value for outlined text would be `3.0`.
|
||||
double? strokeWidth;
|
||||
|
||||
///The text effect.
|
||||
///
|
||||
///The value of this attribute is a [IOSNSAttributedStringTextEffectStyle] object.
|
||||
///The default value of this property is `null`, indicating no text effect.
|
||||
IOSNSAttributedStringTextEffectStyle? textEffect;
|
||||
|
||||
///The color of the underline.
|
||||
///
|
||||
///The value of this attribute is a [Color] object.
|
||||
///The default value is `null`, indicating same as foreground color.
|
||||
Color? underlineColor;
|
||||
|
||||
///The underline style of the text.
|
||||
///
|
||||
///This value indicates whether the text is underlined and corresponds to one of the constants described in [IOSNSUnderlineStyle].
|
||||
///The default value for this attribute is [IOSNSUnderlineStyle.STYLE_NONE].
|
||||
IOSNSUnderlineStyle? underlineStyle;
|
||||
|
||||
IOSNSAttributedString({
|
||||
required this.string,
|
||||
this.backgroundColor,
|
||||
this.baselineOffset,
|
||||
this.expansion,
|
||||
this.foregroundColor,
|
||||
this.kern,
|
||||
this.ligature,
|
||||
this.obliqueness,
|
||||
this.strikethroughColor,
|
||||
this.strikethroughStyle,
|
||||
this.strokeColor,
|
||||
this.strokeWidth,
|
||||
this.textEffect,
|
||||
this.underlineColor,
|
||||
this.underlineStyle,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toMap() {
|
||||
return {
|
||||
"string": this.string,
|
||||
"backgroundColor": this.backgroundColor?.toHex(),
|
||||
"baselineOffset": this.baselineOffset,
|
||||
"expansion": this.expansion,
|
||||
"foregroundColor": this.foregroundColor?.toHex(),
|
||||
"kern": this.kern,
|
||||
"ligature": this.ligature,
|
||||
"obliqueness": this.obliqueness,
|
||||
"strikethroughColor": this.strikethroughColor?.toHex(),
|
||||
"strikethroughStyle": this.strikethroughStyle?.toValue(),
|
||||
"strokeColor": this.strokeColor?.toHex(),
|
||||
"strokeWidth": this.strokeWidth,
|
||||
"textEffect": this.textEffect?.toValue(),
|
||||
"underlineColor": this.underlineColor?.toHex(),
|
||||
"underlineStyle": this.underlineStyle?.toValue(),
|
||||
};
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return this.toMap();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return toMap().toString();
|
||||
}
|
||||
}
|
||||
|
||||
///An iOS-specific Class that represents the constants for the underline style and strikethrough style attribute keys.
|
||||
class IOSNSUnderlineStyle {
|
||||
final int _value;
|
||||
|
||||
const IOSNSUnderlineStyle._internal(this._value);
|
||||
|
||||
static final Set<IOSNSUnderlineStyle> values = [
|
||||
IOSNSUnderlineStyle.STYLE_NONE,
|
||||
IOSNSUnderlineStyle.SINGLE,
|
||||
IOSNSUnderlineStyle.THICK,
|
||||
IOSNSUnderlineStyle.DOUBLE,
|
||||
IOSNSUnderlineStyle.PATTERN_DOT,
|
||||
IOSNSUnderlineStyle.PATTERN_DASH,
|
||||
IOSNSUnderlineStyle.PATTERN_DASH_DOT,
|
||||
IOSNSUnderlineStyle.PATTERN_DASH_DOT_DOT,
|
||||
IOSNSUnderlineStyle.BY_WORD,
|
||||
].toSet();
|
||||
|
||||
static IOSNSUnderlineStyle? fromValue(int? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return IOSNSUnderlineStyle.values
|
||||
.firstWhere((element) => element.toValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
int toValue() => _value;
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
switch (_value) {
|
||||
case 1:
|
||||
return "SINGLE";
|
||||
case 2:
|
||||
return "THICK";
|
||||
case 9:
|
||||
return "DOUBLE";
|
||||
case 256:
|
||||
return "PATTERN_DOT";
|
||||
case 512:
|
||||
return "PATTERN_DASH";
|
||||
case 768:
|
||||
return "PATTERN_DASH_DOT";
|
||||
case 1024:
|
||||
return "PATTERN_DASH_DOT_DOT";
|
||||
case 32768:
|
||||
return "BY_WORD";
|
||||
case 0:
|
||||
default:
|
||||
return "STYLE_NONE";
|
||||
}
|
||||
}
|
||||
|
||||
///Do not draw a line.
|
||||
static const STYLE_NONE =
|
||||
const IOSNSUnderlineStyle._internal(0);
|
||||
|
||||
///Draw a single line.
|
||||
static const SINGLE =
|
||||
const IOSNSUnderlineStyle._internal(1);
|
||||
|
||||
///Draw a thick line.
|
||||
static const THICK =
|
||||
const IOSNSUnderlineStyle._internal(2);
|
||||
|
||||
///Draw a double line.
|
||||
static const DOUBLE =
|
||||
const IOSNSUnderlineStyle._internal(9);
|
||||
|
||||
///Draw a line of dots.
|
||||
static const PATTERN_DOT =
|
||||
const IOSNSUnderlineStyle._internal(256);
|
||||
|
||||
///Draw a line of dashes.
|
||||
static const PATTERN_DASH =
|
||||
const IOSNSUnderlineStyle._internal(512);
|
||||
|
||||
///Draw a line of alternating dashes and dots.
|
||||
static const PATTERN_DASH_DOT =
|
||||
const IOSNSUnderlineStyle._internal(768);
|
||||
|
||||
///Draw a line of alternating dashes and two dots.
|
||||
static const PATTERN_DASH_DOT_DOT =
|
||||
const IOSNSUnderlineStyle._internal(1024);
|
||||
|
||||
///Draw the line only beneath or through words, not whitespace.
|
||||
static const BY_WORD =
|
||||
const IOSNSUnderlineStyle._internal(32768);
|
||||
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
||||
|
||||
///An iOS-specific Class that represents the supported proxy types.
|
||||
class IOSNSAttributedStringTextEffectStyle {
|
||||
final String _value;
|
||||
|
||||
const IOSNSAttributedStringTextEffectStyle._internal(this._value);
|
||||
|
||||
static final Set<IOSNSAttributedStringTextEffectStyle> values = [
|
||||
IOSNSAttributedStringTextEffectStyle.LETTERPRESS_STYLE,
|
||||
].toSet();
|
||||
|
||||
static IOSNSAttributedStringTextEffectStyle? fromValue(String? value) {
|
||||
if (value != null) {
|
||||
try {
|
||||
return IOSNSAttributedStringTextEffectStyle.values
|
||||
.firstWhere((element) => element.toValue() == value);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String toValue() => _value;
|
||||
|
||||
@override
|
||||
String toString() => _value;
|
||||
|
||||
///A graphical text effect that gives glyphs the appearance of letterpress printing, which involves pressing the type into the paper.
|
||||
static const LETTERPRESS_STYLE =
|
||||
const IOSNSAttributedStringTextEffectStyle._internal(
|
||||
"letterpressStyle");
|
||||
|
||||
bool operator ==(value) => value == _value;
|
||||
|
||||
@override
|
||||
int get hashCode => _value.hashCode;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
name: flutter_inappwebview
|
||||
description: A Flutter plugin that allows you to add an inline webview, to use an headless webview, and to open an in-app browser window.
|
||||
version: 5.0.5+3
|
||||
version: 5.1.0
|
||||
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||
|
||||
environment:
|
||||
|
|
Loading…
Reference in New Issue