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">
|
<entry key="file">
|
||||||
<value>
|
<value>
|
||||||
<list>
|
<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" />
|
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/file-6.0.0-nullsafety.4/lib" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
|
@ -377,7 +377,7 @@
|
||||||
<entry key="process">
|
<entry key="process">
|
||||||
<value>
|
<value>
|
||||||
<list>
|
<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" />
|
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/process-4.0.0-nullsafety.4/lib" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</value>
|
||||||
|
@ -441,7 +441,7 @@
|
||||||
<entry key="source_span">
|
<entry key="source_span">
|
||||||
<value>
|
<value>
|
||||||
<list>
|
<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" />
|
<option value="$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/source_span-1.8.0-nullsafety.4/lib" />
|
||||||
</list>
|
</list>
|
||||||
</value>
|
</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/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/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-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/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/glob-1.2.0/lib" />
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/http_multi_server-2.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/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/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-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/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-0.7.5/lib" />
|
||||||
<root url="file://$USER_HOME$/flutter/.pub-cache/hosted/pub.dartlang.org/shelf_packages_handler-2.0.0/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_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_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-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-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/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" />
|
<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
|
## 5.0.5+3
|
||||||
|
|
||||||
- Fixed Android `evaluateJavascript` method when using `contentWorld: ContentWorld.PAGE`
|
- 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:
|
Example:
|
||||||
```dart
|
```dart
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:collection';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:typed_data';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
Future main() async {
|
Future main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
runApp(new MyApp());
|
runApp(new MyApp());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,13 +327,42 @@ class MyApp extends StatefulWidget {
|
||||||
|
|
||||||
class _MyAppState extends State<MyApp> {
|
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 = "";
|
String url = "";
|
||||||
double progress = 0;
|
double progress = 0;
|
||||||
|
final urlController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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
|
@override
|
||||||
|
@ -339,87 +374,131 @@ class _MyAppState extends State<MyApp> {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
home: Scaffold(
|
home: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: Text("Official InAppWebView website")),
|
||||||
title: const Text('InAppWebView Example'),
|
body: SafeArea(
|
||||||
),
|
child: Column(children: <Widget>[
|
||||||
body: Container(
|
TextField(
|
||||||
child: Column(children: <Widget>[
|
decoration: InputDecoration(
|
||||||
Container(
|
prefixIcon: Icon(Icons.search)
|
||||||
padding: EdgeInsets.all(20.0),
|
),
|
||||||
child: Text(
|
controller: urlController,
|
||||||
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
|
keyboardType: TextInputType.url,
|
||||||
),
|
onSubmitted: (value) {
|
||||||
Container(
|
var url = Uri.parse(value);
|
||||||
padding: EdgeInsets.all(10.0),
|
if (url.scheme.isEmpty) {
|
||||||
child: progress < 1.0
|
url = Uri.parse("https://www.google.com/search?q=" + value);
|
||||||
? LinearProgressIndicator(value: progress)
|
}
|
||||||
: Container()),
|
webViewController?.loadUrl(
|
||||||
Expanded(
|
urlRequest: URLRequest(url: url));
|
||||||
child: Container(
|
},
|
||||||
margin: const EdgeInsets.all(10.0),
|
),
|
||||||
decoration:
|
Expanded(
|
||||||
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
|
child: Stack(
|
||||||
child: InAppWebView(
|
children: [
|
||||||
initialUrlRequest: URLRequest(
|
InAppWebView(
|
||||||
url: Uri.parse("https://flutter.dev/")
|
key: webViewKey,
|
||||||
),
|
initialUrlRequest:
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
URLRequest(url: Uri.parse("https://inappwebview.dev/")),
|
||||||
crossPlatform: InAppWebViewOptions(
|
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 (![
|
||||||
ios: IOSInAppWebViewOptions(
|
"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(
|
progress < 1.0
|
||||||
useHybridComposition: true
|
? LinearProgressIndicator(value: progress)
|
||||||
)
|
: Container(),
|
||||||
),
|
],
|
||||||
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;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
ButtonBar(
|
||||||
ButtonBar(
|
alignment: MainAxisAlignment.center,
|
||||||
alignment: MainAxisAlignment.center,
|
children: <Widget>[
|
||||||
children: <Widget>[
|
ElevatedButton(
|
||||||
ElevatedButton(
|
child: Icon(Icons.arrow_back),
|
||||||
child: Icon(Icons.arrow_back),
|
onPressed: () {
|
||||||
onPressed: () {
|
webViewController?.goBack();
|
||||||
webView?.goBack();
|
},
|
||||||
},
|
),
|
||||||
),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
child: Icon(Icons.arrow_forward),
|
||||||
child: Icon(Icons.arrow_forward),
|
onPressed: () {
|
||||||
onPressed: () {
|
webViewController?.goForward();
|
||||||
webView?.goForward();
|
},
|
||||||
},
|
),
|
||||||
),
|
ElevatedButton(
|
||||||
ElevatedButton(
|
child: Icon(Icons.refresh),
|
||||||
child: Icon(Icons.refresh),
|
onPressed: () {
|
||||||
onPressed: () {
|
webViewController?.reload();
|
||||||
webView?.reload();
|
},
|
||||||
},
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
]))),
|
||||||
])),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -428,11 +507,11 @@ class _MyAppState extends State<MyApp> {
|
||||||
Screenshots:
|
Screenshots:
|
||||||
- Android:
|
- 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:
|
||||||
|
|
||||||
![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
|
#### `InAppWebViewController` Methods
|
||||||
|
|
||||||
|
|
|
@ -49,5 +49,6 @@ android {
|
||||||
implementation 'androidx.browser:browser:1.2.0'
|
implementation 'androidx.browser:browser:1.2.0'
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0-rc02'
|
implementation 'androidx.appcompat:appcompat:1.2.0-rc02'
|
||||||
implementation 'com.squareup.okhttp3:mockwebserver:3.14.7'
|
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.InAppWebViewMethodHandler;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.R;
|
import com.pichillilorenzo.flutter_inappwebview.R;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
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.URLRequest;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
import com.pichillilorenzo.flutter_inappwebview.Util;
|
||||||
|
@ -48,6 +50,7 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
||||||
public Integer windowId;
|
public Integer windowId;
|
||||||
public String id;
|
public String id;
|
||||||
public InAppWebView webView;
|
public InAppWebView webView;
|
||||||
|
public PullToRefreshLayout pullToRefreshLayout;
|
||||||
public ActionBar actionBar;
|
public ActionBar actionBar;
|
||||||
public Menu menu;
|
public Menu menu;
|
||||||
public SearchView searchView;
|
public SearchView searchView;
|
||||||
|
@ -74,6 +77,15 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
||||||
|
|
||||||
setContentView(R.layout.activity_web_view);
|
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 = findViewById(R.id.webView);
|
||||||
webView.windowId = windowId;
|
webView.windowId = windowId;
|
||||||
webView.inAppBrowserDelegate = this;
|
webView.inAppBrowserDelegate = this;
|
||||||
|
@ -128,10 +140,10 @@ public class InAppBrowserActivity extends AppCompatActivity implements InAppBrow
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (initialData != null) {
|
else if (initialData != null) {
|
||||||
String mimeType = b.getString("mimeType");
|
String mimeType = b.getString("initialMimeType");
|
||||||
String encoding = b.getString("encoding");
|
String encoding = b.getString("initialEncoding");
|
||||||
String baseUrl = b.getString("baseUrl");
|
String baseUrl = b.getString("initialBaseUrl");
|
||||||
String historyUrl = b.getString("historyUrl");
|
String historyUrl = b.getString("initialHistoryUrl");
|
||||||
webView.loadDataWithBaseURL(baseUrl, initialData, mimeType, encoding, historyUrl);
|
webView.loadDataWithBaseURL(baseUrl, initialData, mimeType, encoding, historyUrl);
|
||||||
}
|
}
|
||||||
else if (initialUrlRequest != null) {
|
else if (initialUrlRequest != null) {
|
||||||
|
|
|
@ -62,44 +62,8 @@ public class InAppBrowserManager implements MethodChannel.MethodCallHandler {
|
||||||
final Activity activity = Shared.activity;
|
final Activity activity = Shared.activity;
|
||||||
|
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "openUrlRequest":
|
case "open":
|
||||||
{
|
open(activity, (Map<String, Object>) call.arguments());
|
||||||
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);
|
|
||||||
}
|
|
||||||
result.success(true);
|
result.success(true);
|
||||||
break;
|
break;
|
||||||
case "openWithSystemBrowser":
|
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,
|
public void open(Activity activity, Map<String, Object> arguments) {
|
||||||
Map<String, Object> contextMenu, Integer windowId, List<Map<String, Object>> initialUserScripts) {
|
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();
|
Bundle extras = new Bundle();
|
||||||
extras.putString("fromActivity", activity.getClass().getName());
|
extras.putString("fromActivity", activity.getClass().getName());
|
||||||
extras.putSerializable("initialUrlRequest", (Serializable) urlRequest);
|
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("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("initialData", data);
|
||||||
extras.putString("initialMimeType", mimeType);
|
extras.putString("initialMimeType", mimeType);
|
||||||
extras.putString("initialEncoding", encoding);
|
extras.putString("initialEncoding", encoding);
|
||||||
extras.putString("initialBaseUrl", baseUrl);
|
extras.putString("initialBaseUrl", baseUrl);
|
||||||
extras.putString("initialHistoryUrl", historyUrl);
|
extras.putString("initialHistoryUrl", historyUrl);
|
||||||
|
extras.putString("id", id);
|
||||||
|
extras.putSerializable("options", (Serializable) options);
|
||||||
extras.putSerializable("contextMenu", (Serializable) contextMenu);
|
extras.putSerializable("contextMenu", (Serializable) contextMenu);
|
||||||
extras.putInt("windowId", windowId != null ? windowId : -1);
|
extras.putInt("windowId", windowId != null ? windowId : -1);
|
||||||
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
|
extras.putSerializable("initialUserScripts", (Serializable) initialUserScripts);
|
||||||
|
extras.putSerializable("pullToRefreshInitialOptions", (Serializable) pullToRefreshInitialOptions);
|
||||||
startInAppBrowserActivity(activity, extras);
|
startInAppBrowserActivity(activity, extras);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class InAppBrowserOptions implements Options<InAppBrowserActivity> {
|
||||||
public Boolean hidden = false;
|
public Boolean hidden = false;
|
||||||
public Boolean hideToolbarTop = false;
|
public Boolean hideToolbarTop = false;
|
||||||
@Nullable
|
@Nullable
|
||||||
public String toolbarTopBackgroundColor = null;
|
public String toolbarTopBackgroundColor;
|
||||||
@Nullable
|
@Nullable
|
||||||
public String toolbarTopFixedTitle;
|
public String toolbarTopFixedTitle;
|
||||||
public Boolean hideUrlBar = false;
|
public Boolean hideUrlBar = false;
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class ContextMenuOptions implements Options<Object> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> getRealOptions(Object webView) {
|
public Map<String, Object> getRealOptions(Object obj) {
|
||||||
Map<String, Object> realOptions = toMap();
|
Map<String, Object> realOptions = toMap();
|
||||||
return realOptions;
|
return realOptions;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,10 @@ import androidx.webkit.WebViewFeature;
|
||||||
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
|
import com.pichillilorenzo.flutter_inappwebview.InAppWebViewMethodHandler;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Shared;
|
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.URLRequest;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
import com.pichillilorenzo.flutter_inappwebview.types.UserScript;
|
||||||
import com.pichillilorenzo.flutter_inappwebview.Util;
|
|
||||||
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
|
import com.pichillilorenzo.flutter_inappwebview.plugin_scripts_js.JavaScriptBridgeJS;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -38,6 +39,7 @@ public class FlutterWebView implements PlatformView {
|
||||||
public InAppWebView webView;
|
public InAppWebView webView;
|
||||||
public final MethodChannel channel;
|
public final MethodChannel channel;
|
||||||
public InAppWebViewMethodHandler methodCallDelegate;
|
public InAppWebViewMethodHandler methodCallDelegate;
|
||||||
|
public PullToRefreshLayout pullToRefreshLayout;
|
||||||
|
|
||||||
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) {
|
public FlutterWebView(BinaryMessenger messenger, final Context context, Object id, HashMap<String, Object> params, View containerView) {
|
||||||
channel = new MethodChannel(messenger, "com.pichillilorenzo/flutter_inappwebview_" + id);
|
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");
|
Map<String, Object> contextMenu = (Map<String, Object>) params.get("contextMenu");
|
||||||
Integer windowId = (Integer) params.get("windowId");
|
Integer windowId = (Integer) params.get("windowId");
|
||||||
List<Map<String, Object>> initialUserScripts = (List<Map<String, Object>>) params.get("initialUserScripts");
|
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();
|
InAppWebViewOptions options = new InAppWebViewOptions();
|
||||||
options.parse(initialOptions);
|
options.parse(initialOptions);
|
||||||
|
@ -74,6 +77,13 @@ public class FlutterWebView implements PlatformView {
|
||||||
webView = new InAppWebView(context, channel, id, windowId, options, contextMenu, containerView, userScripts);
|
webView = new InAppWebView(context, channel, id, windowId, options, contextMenu, containerView, userScripts);
|
||||||
displayListenerProxy.onPostWebViewInitialization(displayManager);
|
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);
|
methodCallDelegate = new InAppWebViewMethodHandler(webView);
|
||||||
channel.setMethodCallHandler(methodCallDelegate);
|
channel.setMethodCallHandler(methodCallDelegate);
|
||||||
|
|
||||||
|
@ -117,7 +127,7 @@ public class FlutterWebView implements PlatformView {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView() {
|
public View getView() {
|
||||||
return webView;
|
return pullToRefreshLayout;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -145,6 +155,11 @@ public class FlutterWebView implements PlatformView {
|
||||||
webView.dispose();
|
webView.dispose();
|
||||||
webView.destroy();
|
webView.destroy();
|
||||||
webView = null;
|
webView = null;
|
||||||
|
|
||||||
|
if (pullToRefreshLayout != null) {
|
||||||
|
pullToRefreshLayout.dispose();
|
||||||
|
pullToRefreshLayout = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
WebSettings settings = webView.getSettings();
|
WebSettings settings = webView.getSettings();
|
||||||
|
|
|
@ -175,7 +175,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
|
|
||||||
channel.invokeMethod("onJsAlert", obj, new MethodChannel.Result() {
|
channel.invokeMethod("onJsAlert", obj, new MethodChannel.Result() {
|
||||||
@Override
|
@Override
|
||||||
public void success(Object response) {
|
public void success(@Nullable Object response) {
|
||||||
String responseMessage = null;
|
String responseMessage = null;
|
||||||
String confirmButtonTitle = null;
|
String confirmButtonTitle = null;
|
||||||
|
|
||||||
|
@ -203,8 +203,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
result.cancel();
|
result.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,8 +290,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
result.cancel();
|
result.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,8 +393,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
result.cancel();
|
result.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -507,8 +507,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
result.cancel();
|
result.cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,6 +599,7 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
||||||
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
if (InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
|
if (InAppWebViewChromeClient.windowWebViewMessages.containsKey(windowId)) {
|
||||||
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
|
InAppWebViewChromeClient.windowWebViewMessages.remove(windowId);
|
||||||
}
|
}
|
||||||
|
@ -638,7 +639,8 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
callback.invoke(origin, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1124,8 +1126,9 @@ public class InAppWebViewChromeClient extends WebChromeClient implements PluginR
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
|
request.deny();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -150,8 +150,8 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, "ERROR: " + s + " " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
|
allowShouldOverrideUrlLoading(webView, url, headers, isForMainFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,8 +375,8 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -428,8 +428,8 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -493,8 +493,8 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -553,8 +553,8 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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, s + ", " + s1);
|
Log.e(LOG_TAG, errorCode + ", " + ((errorMessage != null) ? errorMessage : ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -736,7 +736,7 @@ public class InAppWebViewClient extends WebViewClient {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
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
|
@Override
|
||||||
|
|
|
@ -47,7 +47,7 @@ public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
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
|
@Override
|
||||||
|
@ -79,7 +79,7 @@ public class InAppWebViewRenderProcessClient extends WebViewRenderProcessClient
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void error(String errorCode, @Nullable String errorMessage, @Nullable Object errorDetails) {
|
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
|
@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"
|
tools:context=".in_app_browser.InAppBrowserActivity"
|
||||||
android:focusable="true">
|
android:focusable="true">
|
||||||
|
|
||||||
<com.pichillilorenzo.flutter_inappwebview.in_app_webview.InAppWebView
|
<com.pichillilorenzo.flutter_inappwebview.pull_to_refresh.PullToRefreshLayout
|
||||||
android:id="@+id/webView"
|
android:id="@+id/pullToRefresh"
|
||||||
android:layout_width="match_parent"
|
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
|
<ProgressBar
|
||||||
android:id="@+id/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(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -96,7 +96,7 @@ void main() {
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
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 {
|
testWidgets('set/get options', (WidgetTester tester) async {
|
||||||
|
@ -109,7 +109,7 @@ void main() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
|
@ -318,7 +318,7 @@ void main() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -331,7 +331,7 @@ void main() {
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
var url = await pageLoads.stream.first;
|
var url = await pageLoads.stream.first;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
|
|
||||||
await controller.loadUrl(
|
await controller.loadUrl(
|
||||||
urlRequest: URLRequest(url: Uri.parse('https://www.google.com/')));
|
urlRequest: URLRequest(url: Uri.parse('https://www.google.com/')));
|
||||||
|
@ -353,7 +353,7 @@ void main() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -1528,7 +1528,7 @@ void main() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
ios: IOSInAppWebViewOptions(
|
ios: IOSInAppWebViewOptions(
|
||||||
allowsBackForwardNavigationGestures: true)),
|
allowsBackForwardNavigationGestures: true)),
|
||||||
|
@ -1542,8 +1542,8 @@ void main() {
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
final String? currentUrl = (await controller.getUrl())?.toString();
|
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',
|
testWidgets('target _blank opens in same window',
|
||||||
(WidgetTester tester) async {
|
(WidgetTester tester) async {
|
||||||
|
@ -2497,7 +2497,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(
|
crossPlatform: InAppWebViewOptions(
|
||||||
clearCache: true,
|
clearCache: true,
|
||||||
|
@ -2560,7 +2560,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -2634,7 +2634,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onLoadStop: (controller, url) async {
|
onLoadStop: (controller, url) async {
|
||||||
await controller.evaluateJavascript(source: "window.print();");
|
await controller.evaluateJavascript(source: "window.print();");
|
||||||
},
|
},
|
||||||
|
@ -2645,7 +2645,7 @@ setTimeout(function() {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final String url = await onPrintCompleter.future;
|
final String url = await onPrintCompleter.future;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
}, skip: true);
|
}, skip: true);
|
||||||
|
|
||||||
testWidgets('onWindowFocus', (WidgetTester tester) async {
|
testWidgets('onWindowFocus', (WidgetTester tester) async {
|
||||||
|
@ -2656,7 +2656,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onLoadStop: (controller, url) async {
|
onLoadStop: (controller, url) async {
|
||||||
await controller.evaluateJavascript(
|
await controller.evaluateJavascript(
|
||||||
source: 'window.dispatchEvent(new Event("focus"));');
|
source: 'window.dispatchEvent(new Event("focus"));');
|
||||||
|
@ -2678,7 +2678,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onLoadStop: (controller, url) async {
|
onLoadStop: (controller, url) async {
|
||||||
await controller.evaluateJavascript(
|
await controller.evaluateJavascript(
|
||||||
source: 'window.dispatchEvent(new Event("blur"));');
|
source: 'window.dispatchEvent(new Event("blur"));');
|
||||||
|
@ -2703,7 +2703,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -2715,7 +2715,7 @@ setTimeout(function() {
|
||||||
);
|
);
|
||||||
|
|
||||||
final String? url = await onPageCommitVisibleCompleter.future;
|
final String? url = await onPageCommitVisibleCompleter.future;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('onTitleChanged', (WidgetTester tester) async {
|
testWidgets('onTitleChanged', (WidgetTester tester) async {
|
||||||
|
@ -2729,7 +2729,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -2903,7 +2903,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -2973,7 +2973,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -3019,7 +3019,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)),
|
ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
|
@ -3039,7 +3039,7 @@ setTimeout(function() {
|
||||||
|
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
final String url = await onNavigationResponseCompleter.future;
|
final String url = await onNavigationResponseCompleter.future;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
}, skip: !Platform.isIOS);
|
}, skip: !Platform.isIOS);
|
||||||
|
|
||||||
testWidgets('cancel navigation', (WidgetTester tester) async {
|
testWidgets('cancel navigation', (WidgetTester tester) async {
|
||||||
|
@ -3055,7 +3055,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)),
|
ios: IOSInAppWebViewOptions(useOnNavigationResponse: true)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
|
@ -3074,7 +3074,7 @@ setTimeout(function() {
|
||||||
);
|
);
|
||||||
|
|
||||||
final String url = await onNavigationResponseCompleter.future;
|
final String url = await onNavigationResponseCompleter.future;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
expect(pageLoaded.future, doesNotComplete);
|
expect(pageLoaded.future, doesNotComplete);
|
||||||
}, skip: !Platform.isIOS);
|
}, skip: !Platform.isIOS);
|
||||||
}, skip: !Platform.isIOS);
|
}, skip: !Platform.isIOS);
|
||||||
|
@ -3374,7 +3374,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev/')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -3387,11 +3387,11 @@ setTimeout(function() {
|
||||||
final InAppWebViewController controller =
|
final InAppWebViewController controller =
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
String? url = await pageLoads.stream.first;
|
String? url = await pageLoads.stream.first;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
|
|
||||||
await controller.reload();
|
await controller.reload();
|
||||||
url = await pageLoads.stream.first;
|
url = await pageLoads.stream.first;
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
|
|
||||||
pageLoads.close();
|
pageLoads.close();
|
||||||
});
|
});
|
||||||
|
@ -3443,6 +3443,7 @@ setTimeout(function() {
|
||||||
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
||||||
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
||||||
|
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
await controller.goBack();
|
await controller.goBack();
|
||||||
url = await pageLoads.stream.first;
|
url = await pageLoads.stream.first;
|
||||||
webHistory = await controller.getCopyBackForwardList();
|
webHistory = await controller.getCopyBackForwardList();
|
||||||
|
@ -3456,6 +3457,7 @@ setTimeout(function() {
|
||||||
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
||||||
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
||||||
|
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
await controller.goForward();
|
await controller.goForward();
|
||||||
url = await pageLoads.stream.first;
|
url = await pageLoads.stream.first;
|
||||||
webHistory = await controller.getCopyBackForwardList();
|
webHistory = await controller.getCopyBackForwardList();
|
||||||
|
@ -3469,6 +3471,7 @@ setTimeout(function() {
|
||||||
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
expect(webHistory.list![0].url.toString(), 'https://flutter.dev/');
|
||||||
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
expect(webHistory.list![1].url.toString(), 'https://github.com/flutter');
|
||||||
|
|
||||||
|
await Future.delayed(Duration(seconds: 1));
|
||||||
await controller.goTo(historyItem: webHistory.list![0]);
|
await controller.goTo(historyItem: webHistory.list![0]);
|
||||||
url = await pageLoads.stream.first;
|
url = await pageLoads.stream.first;
|
||||||
webHistory = await controller.getCopyBackForwardList();
|
webHistory = await controller.getCopyBackForwardList();
|
||||||
|
@ -4392,6 +4395,36 @@ setTimeout(function() {
|
||||||
expect(await InAppWebViewController.getDefaultUserAgent(), isNotNull);
|
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', () {
|
group('android methods', () {
|
||||||
testWidgets('clearSslPreferences', (WidgetTester tester) async {
|
testWidgets('clearSslPreferences', (WidgetTester tester) async {
|
||||||
final Completer controllerCompleter =
|
final Completer controllerCompleter =
|
||||||
|
@ -4462,7 +4495,7 @@ setTimeout(function() {
|
||||||
child: InAppWebView(
|
child: InAppWebView(
|
||||||
key: GlobalKey(),
|
key: GlobalKey(),
|
||||||
initialUrlRequest:
|
initialUrlRequest:
|
||||||
URLRequest(url: Uri.parse('https://flutter.dev')),
|
URLRequest(url: Uri.parse('https://github.com/flutter')),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -4477,7 +4510,7 @@ setTimeout(function() {
|
||||||
await controllerCompleter.future;
|
await controllerCompleter.future;
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
var originUrl = (await controller.android.getOriginalUrl())?.toString();
|
var originUrl = (await controller.android.getOriginalUrl())?.toString();
|
||||||
expect(originUrl, 'https://flutter.dev/');
|
expect(originUrl, 'https://github.com/flutter');
|
||||||
}, skip: !Platform.isAndroid);
|
}, skip: !Platform.isAndroid);
|
||||||
|
|
||||||
testWidgets('pageDown/pageUp', (WidgetTester tester) async {
|
testWidgets('pageDown/pageUp', (WidgetTester tester) async {
|
||||||
|
@ -4759,7 +4792,7 @@ setTimeout(function() {
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
|
|
||||||
var headlessWebView = new HeadlessInAppWebView(
|
var headlessWebView = new HeadlessInAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
|
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
controllerCompleter.complete(controller);
|
controllerCompleter.complete(controller);
|
||||||
},
|
},
|
||||||
|
@ -4774,7 +4807,7 @@ setTimeout(function() {
|
||||||
await pageLoaded.future;
|
await pageLoaded.future;
|
||||||
|
|
||||||
final String? url = (await controller.getUrl())?.toString();
|
final String? url = (await controller.getUrl())?.toString();
|
||||||
expect(url, 'https://flutter.dev/');
|
expect(url, 'https://github.com/flutter');
|
||||||
|
|
||||||
await headlessWebView.dispose();
|
await headlessWebView.dispose();
|
||||||
|
|
||||||
|
@ -4787,7 +4820,7 @@ setTimeout(function() {
|
||||||
final Completer<void> pageLoaded = Completer<void>();
|
final Completer<void> pageLoaded = Completer<void>();
|
||||||
|
|
||||||
var headlessWebView = new HeadlessInAppWebView(
|
var headlessWebView = new HeadlessInAppWebView(
|
||||||
initialUrlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
|
initialUrlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
initialOptions: InAppWebViewGroupOptions(
|
initialOptions: InAppWebViewGroupOptions(
|
||||||
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
crossPlatform: InAppWebViewOptions(javaScriptEnabled: false)),
|
||||||
onWebViewCreated: (controller) {
|
onWebViewCreated: (controller) {
|
||||||
|
@ -4818,7 +4851,7 @@ setTimeout(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
group('InAppBrowser', () {
|
group('InAppBrowser', () {
|
||||||
test('open and close', () async {
|
test('openUrlRequest and close', () async {
|
||||||
var inAppBrowser = new MyInAppBrowser();
|
var inAppBrowser = new MyInAppBrowser();
|
||||||
expect(inAppBrowser.isOpened(), false);
|
expect(inAppBrowser.isOpened(), false);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
|
@ -4826,13 +4859,87 @@ setTimeout(function() {
|
||||||
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
}, throwsA(isInstanceOf<InAppBrowserNotOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.openUrlRequest(
|
await inAppBrowser.openUrlRequest(
|
||||||
urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")));
|
urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")));
|
||||||
await inAppBrowser.browserCreated.future;
|
await inAppBrowser.browserCreated.future;
|
||||||
expect(inAppBrowser.isOpened(), true);
|
expect(inAppBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await inAppBrowser.openUrlRequest(
|
await inAppBrowser.openUrlRequest(
|
||||||
urlRequest:
|
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>()));
|
}, throwsA(isInstanceOf<InAppBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await inAppBrowser.firstPageLoaded.future;
|
await inAppBrowser.firstPageLoaded.future;
|
||||||
|
@ -4850,7 +4957,7 @@ setTimeout(function() {
|
||||||
test('set/get options', () async {
|
test('set/get options', () async {
|
||||||
var inAppBrowser = new MyInAppBrowser();
|
var inAppBrowser = new MyInAppBrowser();
|
||||||
await inAppBrowser.openUrlRequest(
|
await inAppBrowser.openUrlRequest(
|
||||||
urlRequest: URLRequest(url: Uri.parse("https://flutter.dev")),
|
urlRequest: URLRequest(url: Uri.parse("https://github.com/flutter")),
|
||||||
options: InAppBrowserClassOptions(
|
options: InAppBrowserClassOptions(
|
||||||
crossPlatform: InAppBrowserOptions(hideToolbarTop: true)));
|
crossPlatform: InAppBrowserOptions(hideToolbarTop: true)));
|
||||||
await inAppBrowser.browserCreated.future;
|
await inAppBrowser.browserCreated.future;
|
||||||
|
@ -4875,12 +4982,12 @@ setTimeout(function() {
|
||||||
var chromeSafariBrowser = new MyChromeSafariBrowser();
|
var chromeSafariBrowser = new MyChromeSafariBrowser();
|
||||||
expect(chromeSafariBrowser.isOpened(), false);
|
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;
|
await chromeSafariBrowser.browserCreated.future;
|
||||||
expect(chromeSafariBrowser.isOpened(), true);
|
expect(chromeSafariBrowser.isOpened(), true);
|
||||||
expect(() async {
|
expect(() async {
|
||||||
await chromeSafariBrowser.open(
|
await chromeSafariBrowser.open(
|
||||||
url: Uri.parse("https://github.com/flutter"));
|
url: Uri.parse("https://flutter.dev"));
|
||||||
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
}, throwsA(isInstanceOf<ChromeSafariBrowserAlreadyOpenedException>()));
|
||||||
|
|
||||||
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
await expectLater(chromeSafariBrowser.firstPageLoaded.future, completes);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
@ -23,16 +24,21 @@ class MyInAppBrowser extends InAppBrowser {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future onLoadStop(url) async {
|
Future onLoadStop(url) async {
|
||||||
|
pullToRefreshController?.endRefreshing();
|
||||||
print("\n\nStopped $url\n\n");
|
print("\n\nStopped $url\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onLoadError(url, code, message) {
|
void onLoadError(url, code, message) {
|
||||||
|
pullToRefreshController?.endRefreshing();
|
||||||
print("Can't load $url.. Error: $message");
|
print("Can't load $url.. Error: $message");
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onProgressChanged(progress) {
|
void onProgressChanged(progress) {
|
||||||
|
if (progress == 100) {
|
||||||
|
pullToRefreshController?.endRefreshing();
|
||||||
|
}
|
||||||
print("Progress: $progress");
|
print("Progress: $progress");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,9 +83,27 @@ class InAppBrowserExampleScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
class _InAppBrowserExampleScreenState extends State<InAppBrowserExampleScreen> {
|
||||||
|
|
||||||
|
late PullToRefreshController pullToRefreshController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.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
|
@override
|
||||||
|
|
|
@ -17,13 +17,26 @@ class InAppWebViewExampleScreen extends StatefulWidget {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
|
|
||||||
final GlobalKey webViewKey = GlobalKey();
|
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;
|
late ContextMenu contextMenu;
|
||||||
String url = "";
|
String url = "";
|
||||||
double progress = 0;
|
double progress = 0;
|
||||||
// CookieManager _cookieManager = CookieManager.instance();
|
final urlController = TextEditingController();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -37,15 +50,15 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
title: "Special",
|
title: "Special",
|
||||||
action: () async {
|
action: () async {
|
||||||
print("Menu item Special clicked!");
|
print("Menu item Special clicked!");
|
||||||
print(await webView?.getSelectedText());
|
print(await webViewController?.getSelectedText());
|
||||||
await webView?.clearFocus();
|
await webViewController?.clearFocus();
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: false),
|
options: ContextMenuOptions(hideDefaultSystemContextMenuItems: false),
|
||||||
onCreateContextMenu: (hitTestResult) async {
|
onCreateContextMenu: (hitTestResult) async {
|
||||||
print("onCreateContextMenu");
|
print("onCreateContextMenu");
|
||||||
print(hitTestResult.extra);
|
print(hitTestResult.extra);
|
||||||
print(await webView?.getSelectedText());
|
print(await webViewController?.getSelectedText());
|
||||||
},
|
},
|
||||||
onHideContextMenu: () {
|
onHideContextMenu: () {
|
||||||
print("onHideContextMenu");
|
print("onHideContextMenu");
|
||||||
|
@ -59,6 +72,20 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
" " +
|
" " +
|
||||||
contextMenuItemClicked.title);
|
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
|
@override
|
||||||
|
@ -66,18 +93,6 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
var options = InAppWebViewGroupOptions(
|
|
||||||
crossPlatform: InAppWebViewOptions(
|
|
||||||
useShouldOverrideUrlLoading: false,
|
|
||||||
mediaPlaybackRequiresUserGesture: false,
|
|
||||||
),
|
|
||||||
android: AndroidInAppWebViewOptions(
|
|
||||||
useHybridComposition: true,
|
|
||||||
),
|
|
||||||
ios: IOSInAppWebViewOptions(
|
|
||||||
allowsInlineMediaPlayback: true,
|
|
||||||
));
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
|
@ -85,92 +100,106 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
drawer: myDrawer(context: context),
|
drawer: myDrawer(context: context),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(children: <Widget>[
|
child: Column(children: <Widget>[
|
||||||
Container(
|
TextField(
|
||||||
padding: EdgeInsets.all(20.0),
|
decoration: InputDecoration(
|
||||||
child: Text(
|
prefixIcon: Icon(Icons.search)
|
||||||
"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);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
|
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(
|
ButtonBar(
|
||||||
alignment: MainAxisAlignment.center,
|
alignment: MainAxisAlignment.center,
|
||||||
|
@ -178,19 +207,19 @@ class _InAppWebViewExampleScreenState extends State<InAppWebViewExampleScreen> {
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Icon(Icons.arrow_back),
|
child: Icon(Icons.arrow_back),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
webView?.goBack();
|
webViewController?.goBack();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Icon(Icons.arrow_forward),
|
child: Icon(Icons.arrow_forward),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
webView?.goForward();
|
webViewController?.goForward();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
ElevatedButton(
|
ElevatedButton(
|
||||||
child: Icon(Icons.refresh),
|
child: Icon(Icons.refresh),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
webView?.reload();
|
webViewController?.reload();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -22,7 +22,7 @@ dependencies:
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
flutter_downloader: ^1.5.2
|
flutter_downloader: ^1.5.2
|
||||||
path_provider: ^2.0.0-nullsafety
|
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
|
url_launcher: ^6.0.0-nullsafety.4
|
||||||
# connectivity: ^0.4.5+6
|
# connectivity: ^0.4.5+6
|
||||||
flutter_inappwebview:
|
flutter_inappwebview:
|
||||||
|
|
|
@ -36,38 +36,8 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
||||||
let arguments = call.arguments as? NSDictionary
|
let arguments = call.arguments as? NSDictionary
|
||||||
|
|
||||||
switch call.method {
|
switch call.method {
|
||||||
case "openUrlRequest":
|
case "open":
|
||||||
let id = arguments!["id"] as! String
|
open(arguments: arguments!)
|
||||||
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)
|
|
||||||
result(true)
|
result(true)
|
||||||
break
|
break
|
||||||
case "openWithSystemBrowser":
|
case "openWithSystemBrowser":
|
||||||
|
@ -98,37 +68,25 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
||||||
return webViewController
|
return webViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
public func openUrlRequest(id: String, urlRequest: [String:Any?], options: [String: Any?],
|
public func open(arguments: NSDictionary) {
|
||||||
contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
|
let id = arguments["id"] as! String
|
||||||
let webViewController = prepareInAppBrowserWebViewController(options: options)
|
let urlRequest = arguments["urlRequest"] as? [String:Any?]
|
||||||
|
let assetFilePath = arguments["assetFilePath"] as? String
|
||||||
webViewController.id = id
|
let data = arguments["data"] as? String
|
||||||
webViewController.initialUrlRequest = URLRequest.init(fromPluginMap: urlRequest)
|
let mimeType = arguments["mimeType"] as? String
|
||||||
webViewController.contextMenu = contextMenu
|
let encoding = arguments["encoding"] as? String
|
||||||
webViewController.windowId = windowId
|
let baseUrl = arguments["baseUrl"] as? String
|
||||||
webViewController.initialUserScripts = initialUserScripts ?? []
|
let options = arguments["options"] as! [String: Any?]
|
||||||
|
let contextMenu = arguments["contextMenu"] as! [String: Any]
|
||||||
presentViewController(webViewController: webViewController)
|
let windowId = arguments["windowId"] as? Int64
|
||||||
}
|
let initialUserScripts = arguments["initialUserScripts"] as? [[String: Any]]
|
||||||
|
let pullToRefreshInitialOptions = arguments["pullToRefreshOptions"] as! [String: Any?]
|
||||||
public func openFile(id: String, assetFilePath: String, options: [String: Any?],
|
|
||||||
contextMenu: [String: Any], windowId: Int64?, initialUserScripts: [[String: Any]]?) {
|
|
||||||
let webViewController = prepareInAppBrowserWebViewController(options: options)
|
let webViewController = prepareInAppBrowserWebViewController(options: options)
|
||||||
|
|
||||||
webViewController.id = id
|
webViewController.id = id
|
||||||
|
webViewController.initialUrlRequest = urlRequest != nil ? URLRequest.init(fromPluginMap: urlRequest!) : nil
|
||||||
webViewController.initialFile = assetFilePath
|
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.initialData = data
|
||||||
webViewController.initialMimeType = mimeType
|
webViewController.initialMimeType = mimeType
|
||||||
webViewController.initialEncoding = encoding
|
webViewController.initialEncoding = encoding
|
||||||
|
@ -136,6 +94,7 @@ public class InAppBrowserManager: NSObject, FlutterPlugin {
|
||||||
webViewController.contextMenu = contextMenu
|
webViewController.contextMenu = contextMenu
|
||||||
webViewController.windowId = windowId
|
webViewController.windowId = windowId
|
||||||
webViewController.initialUserScripts = initialUserScripts ?? []
|
webViewController.initialUserScripts = initialUserScripts ?? []
|
||||||
|
webViewController.pullToRefreshInitialOptions = pullToRefreshInitialOptions
|
||||||
|
|
||||||
presentViewController(webViewController: webViewController)
|
presentViewController(webViewController: webViewController)
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
||||||
var initialBaseUrl: String?
|
var initialBaseUrl: String?
|
||||||
var previousStatusBarStyle = -1
|
var previousStatusBarStyle = -1
|
||||||
var initialUserScripts: [[String: Any]] = []
|
var initialUserScripts: [[String: Any]] = []
|
||||||
|
var pullToRefreshInitialOptions: [String: Any?] = [:]
|
||||||
var methodCallDelegate: InAppWebViewMethodHandler?
|
var methodCallDelegate: InAppWebViewMethodHandler?
|
||||||
|
|
||||||
public override func loadView() {
|
public override func loadView() {
|
||||||
|
@ -64,6 +65,15 @@ public class InAppBrowserWebViewController: UIViewController, InAppBrowserDelega
|
||||||
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
|
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
|
||||||
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
|
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()
|
prepareWebView()
|
||||||
|
|
||||||
progressBar = UIProgressView(progressViewStyle: .bar)
|
progressBar = UIProgressView(progressViewStyle: .bar)
|
||||||
|
|
|
@ -23,13 +23,8 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
||||||
self.registrar = registrar
|
self.registrar = registrar
|
||||||
self.viewId = viewId
|
self.viewId = viewId
|
||||||
|
|
||||||
var channelName = ""
|
channel = FlutterMethodChannel(name: "com.pichillilorenzo/flutter_inappwebview_" + String(describing: viewId),
|
||||||
if let id = viewId as? Int64 {
|
binaryMessenger: registrar.messenger())
|
||||||
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())
|
|
||||||
|
|
||||||
myView = UIView(frame: frame)
|
myView = UIView(frame: frame)
|
||||||
myView!.clipsToBounds = true
|
myView!.clipsToBounds = true
|
||||||
|
@ -41,6 +36,7 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
||||||
let contextMenu = args["contextMenu"] as? [String: Any]
|
let contextMenu = args["contextMenu"] as? [String: Any]
|
||||||
let windowId = args["windowId"] as? Int64
|
let windowId = args["windowId"] as? Int64
|
||||||
let initialUserScripts = args["initialUserScripts"] as? [[String: Any]]
|
let initialUserScripts = args["initialUserScripts"] as? [[String: Any]]
|
||||||
|
let pullToRefreshInitialOptions = args["pullToRefreshOptions"] as! [String: Any?]
|
||||||
|
|
||||||
var userScripts: [UserScript] = []
|
var userScripts: [UserScript] = []
|
||||||
if let initialUserScripts = initialUserScripts {
|
if let initialUserScripts = initialUserScripts {
|
||||||
|
@ -70,6 +66,15 @@ public class FlutterWebViewController: NSObject, FlutterPlatformView {
|
||||||
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
|
methodCallDelegate = InAppWebViewMethodHandler(webView: webView!)
|
||||||
channel!.setMethodCallHandler(LeakAvoider(delegate: methodCallDelegate!).handle)
|
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]
|
webView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
myView!.autoresizesSubviews = true
|
myView!.autoresizesSubviews = true
|
||||||
myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
myView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
|
||||||
|
|
|
@ -9,16 +9,20 @@ import Flutter
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebKit
|
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 windowId: Int64?
|
||||||
var inAppBrowserDelegate: InAppBrowserDelegate?
|
var inAppBrowserDelegate: InAppBrowserDelegate?
|
||||||
var channel: FlutterMethodChannel?
|
var channel: FlutterMethodChannel?
|
||||||
var options: InAppWebViewOptions?
|
var options: InAppWebViewOptions?
|
||||||
|
var pullToRefreshControl: PullToRefreshControl?
|
||||||
|
|
||||||
static var sslCertificatesMap: [String: SslCertificate] = [:] // [URL host name : SslCertificate]
|
static var sslCertificatesMap: [String: SslCertificate] = [:] // [URL host name : SslCertificate]
|
||||||
static var credentialsProposed: [URLCredential] = []
|
static var credentialsProposed: [URLCredential] = []
|
||||||
|
|
||||||
var lastScrollX: CGFloat = 0
|
var lastScrollX: CGFloat = 0
|
||||||
var lastScrollY: CGFloat = 0
|
var lastScrollY: CGFloat = 0
|
||||||
|
|
||||||
var isPausedTimers = false
|
var isPausedTimers = false
|
||||||
var isPausedTimersCompletionHandler: (() -> Void)?
|
var isPausedTimersCompletionHandler: (() -> Void)?
|
||||||
|
|
||||||
|
@ -251,9 +255,18 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
return super.canPerformAction(action, withSender: sender)
|
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() {
|
public func prepare() {
|
||||||
self.scrollView.addGestureRecognizer(self.longPressRecognizer)
|
scrollView.addGestureRecognizer(self.longPressRecognizer)
|
||||||
self.scrollView.addGestureRecognizer(self.recognizerForDisablingContextMenuOnLinks)
|
scrollView.addGestureRecognizer(self.recognizerForDisablingContextMenuOnLinks)
|
||||||
|
scrollView.addObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset), options: [.new, .old], context: nil)
|
||||||
|
|
||||||
addObserver(self,
|
addObserver(self,
|
||||||
forKeyPath: #keyPath(WKWebView.estimatedProgress),
|
forKeyPath: #keyPath(WKWebView.estimatedProgress),
|
||||||
|
@ -276,7 +289,6 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
name: UIMenuController.willShowMenuNotification,
|
name: UIMenuController.willShowMenuNotification,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
NotificationCenter.default.addObserver(
|
||||||
self,
|
self,
|
||||||
selector: #selector(onHideContextMenu),
|
selector: #selector(onHideContextMenu),
|
||||||
|
@ -566,7 +578,13 @@ public class InAppWebView: WKWebView, UIScrollViewDelegate, WKUIDelegate, WKNavi
|
||||||
let newTitle = change?[NSKeyValueChangeKey.newKey] as? String
|
let newTitle = change?[NSKeyValueChangeKey.newKey] as? String
|
||||||
onTitleChanged(title: newTitle)
|
onTitleChanged(title: newTitle)
|
||||||
inAppBrowserDelegate?.didChangeTitle(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()
|
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 disableVerticalScroll = options?.disableVerticalScroll ?? false
|
||||||
let disableHorizontalScroll = options?.disableHorizontalScroll ?? false
|
let disableHorizontalScroll = options?.disableHorizontalScroll ?? false
|
||||||
if disableVerticalScroll && disableHorizontalScroll {
|
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() {
|
public func dispose() {
|
||||||
if isPausedTimers, let completionHandler = isPausedTimersCompletionHandler {
|
if isPausedTimers, let completionHandler = isPausedTimersCompletionHandler {
|
||||||
isPausedTimersCompletionHandler = nil
|
isPausedTimersCompletionHandler = nil
|
||||||
|
@ -2659,12 +2702,16 @@ if(window.\(JAVASCRIPT_BRIDGE_NAME)[\(_callHandlerID)] != null) {
|
||||||
for imp in customIMPs {
|
for imp in customIMPs {
|
||||||
imp_removeBlock(imp)
|
imp_removeBlock(imp)
|
||||||
}
|
}
|
||||||
|
scrollView.removeObserver(self, forKeyPath: #keyPath(UIScrollView.contentOffset))
|
||||||
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
|
longPressRecognizer.removeTarget(self, action: #selector(longPressGestureDetected))
|
||||||
longPressRecognizer.delegate = nil
|
longPressRecognizer.delegate = nil
|
||||||
scrollView.removeGestureRecognizer(longPressRecognizer)
|
scrollView.removeGestureRecognizer(longPressRecognizer)
|
||||||
recognizerForDisablingContextMenuOnLinks.removeTarget(self, action: #selector(longPressGestureDetected))
|
recognizerForDisablingContextMenuOnLinks.removeTarget(self, action: #selector(longPressGestureDetected))
|
||||||
recognizerForDisablingContextMenuOnLinks.delegate = nil
|
recognizerForDisablingContextMenuOnLinks.delegate = nil
|
||||||
scrollView.removeGestureRecognizer(recognizerForDisablingContextMenuOnLinks)
|
scrollView.removeGestureRecognizer(recognizerForDisablingContextMenuOnLinks)
|
||||||
|
disablePullToRefresh()
|
||||||
|
pullToRefreshControl?.dispose()
|
||||||
|
pullToRefreshControl = nil
|
||||||
uiDelegate = nil
|
uiDelegate = nil
|
||||||
navigationDelegate = nil
|
navigationDelegate = nil
|
||||||
scrollView.delegate = nil
|
scrollView.delegate = nil
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
import WebKit
|
import WebKit
|
||||||
|
|
||||||
class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
public class InAppWebViewMethodHandler: FlutterMethodCallDelegate {
|
||||||
var webView: InAppWebView?
|
var webView: InAppWebView?
|
||||||
|
|
||||||
init(webView: InAppWebView) {
|
init(webView: InAppWebView) {
|
||||||
|
|
|
@ -11,8 +11,8 @@ public class LeakAvoider: NSObject {
|
||||||
weak var delegate : FlutterMethodCallDelegate?
|
weak var delegate : FlutterMethodCallDelegate?
|
||||||
|
|
||||||
init(delegate: FlutterMethodCallDelegate) {
|
init(delegate: FlutterMethodCallDelegate) {
|
||||||
self.delegate = delegate
|
|
||||||
super.init()
|
super.init()
|
||||||
|
self.delegate = delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
|
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 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
import 'package:flutter_inappwebview/src/util.dart';
|
import 'package:flutter_inappwebview/src/util.dart';
|
||||||
|
|
||||||
import '../context_menu.dart';
|
import '../context_menu.dart';
|
||||||
|
@ -40,14 +41,17 @@ class InAppBrowserNotOpenedException implements Exception {
|
||||||
///This class uses the native WebView of the platform.
|
///This class uses the native WebView of the platform.
|
||||||
///The [webViewController] field can be used to access the [InAppWebViewController] API.
|
///The [webViewController] field can be used to access the [InAppWebViewController] API.
|
||||||
class InAppBrowser {
|
class InAppBrowser {
|
||||||
///Browser's UUID.
|
///View ID.
|
||||||
late String id;
|
late final String id;
|
||||||
|
|
||||||
///Context menu used by the browser. It should be set before opening the browser.
|
///Context menu used by the browser. It should be set before opening the browser.
|
||||||
ContextMenu? contextMenu;
|
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.
|
///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;
|
bool _isOpened = false;
|
||||||
late MethodChannel _channel;
|
late MethodChannel _channel;
|
||||||
|
@ -68,13 +72,14 @@ class InAppBrowser {
|
||||||
this._channel.setMethodCallHandler(handleMethod);
|
this._channel.setMethodCallHandler(handleMethod);
|
||||||
_isOpened = false;
|
_isOpened = false;
|
||||||
webViewController = new InAppWebViewController.fromInAppBrowser(
|
webViewController = new InAppWebViewController.fromInAppBrowser(
|
||||||
id, this._channel, this, this.initialUserScripts);
|
this._channel, this, this.initialUserScripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<dynamic> handleMethod(MethodCall call) async {
|
Future<dynamic> handleMethod(MethodCall call) async {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "onBrowserCreated":
|
case "onBrowserCreated":
|
||||||
this._isOpened = true;
|
this._isOpened = true;
|
||||||
|
this.pullToRefreshController?.initMethodChannel(id);
|
||||||
onBrowserCreated();
|
onBrowserCreated();
|
||||||
break;
|
break;
|
||||||
case "onExit":
|
case "onExit":
|
||||||
|
@ -106,7 +111,8 @@ class InAppBrowser {
|
||||||
args.putIfAbsent('windowId', () => windowId);
|
args.putIfAbsent('windowId', () => windowId);
|
||||||
args.putIfAbsent('initialUserScripts',
|
args.putIfAbsent('initialUserScripts',
|
||||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
() => 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.
|
///Opens the given [assetFilePath] file in a new [InAppBrowser] instance.
|
||||||
|
@ -159,7 +165,8 @@ class InAppBrowser {
|
||||||
args.putIfAbsent('windowId', () => windowId);
|
args.putIfAbsent('windowId', () => windowId);
|
||||||
args.putIfAbsent('initialUserScripts',
|
args.putIfAbsent('initialUserScripts',
|
||||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
() => 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.
|
///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('windowId', () => windowId);
|
||||||
args.putIfAbsent('initialUserScripts',
|
args.putIfAbsent('initialUserScripts',
|
||||||
() => initialUserScripts?.map((e) => e.toMap()).toList() ?? []);
|
() => 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!
|
///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 'webview.dart';
|
||||||
import 'in_app_webview_controller.dart';
|
import 'in_app_webview_controller.dart';
|
||||||
import 'in_app_webview_options.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.
|
///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.
|
///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.
|
///Remember to dispose it when you don't need it anymore.
|
||||||
class HeadlessInAppWebView implements WebView {
|
class HeadlessInAppWebView implements WebView {
|
||||||
late String id;
|
///View ID.
|
||||||
|
late final String id;
|
||||||
|
|
||||||
bool _isDisposed = true;
|
bool _isDisposed = true;
|
||||||
static const MethodChannel _sharedChannel =
|
static const MethodChannel _sharedChannel =
|
||||||
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
|
const MethodChannel('com.pichillilorenzo/flutter_headless_inappwebview');
|
||||||
|
@ -85,7 +89,8 @@ class HeadlessInAppWebView implements WebView {
|
||||||
this.initialData,
|
this.initialData,
|
||||||
this.initialOptions,
|
this.initialOptions,
|
||||||
this.contextMenu,
|
this.contextMenu,
|
||||||
this.initialUserScripts}) {
|
this.initialUserScripts,
|
||||||
|
this.pullToRefreshController}) {
|
||||||
id = ViewIdGenerator.generateId();
|
id = ViewIdGenerator.generateId();
|
||||||
webViewController = new InAppWebViewController(id, this);
|
webViewController = new InAppWebViewController(id, this);
|
||||||
}
|
}
|
||||||
|
@ -93,6 +98,7 @@ class HeadlessInAppWebView implements WebView {
|
||||||
Future<dynamic> handleMethod(MethodCall call) async {
|
Future<dynamic> handleMethod(MethodCall call) async {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
case "onHeadlessWebViewCreated":
|
case "onHeadlessWebViewCreated":
|
||||||
|
pullToRefreshController?.initMethodChannel(id);
|
||||||
if (onWebViewCreated != null) {
|
if (onWebViewCreated != null) {
|
||||||
onWebViewCreated!(webViewController);
|
onWebViewCreated!(webViewController);
|
||||||
}
|
}
|
||||||
|
@ -123,6 +129,7 @@ class HeadlessInAppWebView implements WebView {
|
||||||
'windowId': this.windowId,
|
'windowId': this.windowId,
|
||||||
'initialUserScripts':
|
'initialUserScripts':
|
||||||
this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
this.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||||
|
'pullToRefreshOptions': this.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||||
});
|
});
|
||||||
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
|
await _sharedChannel.invokeMethod('createHeadlessWebView', args);
|
||||||
}
|
}
|
||||||
|
@ -177,6 +184,9 @@ class HeadlessInAppWebView implements WebView {
|
||||||
@override
|
@override
|
||||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final PullToRefreshController? pullToRefreshController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final void Function(InAppWebViewController controller, Uri? url)?
|
final void Function(InAppWebViewController controller, Uri? url)?
|
||||||
onPageCommitVisible;
|
onPageCommitVisible;
|
||||||
|
|
|
@ -8,6 +8,7 @@ import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
|
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||||
|
|
||||||
import '../context_menu.dart';
|
import '../context_menu.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
|
@ -15,6 +16,7 @@ import '../types.dart';
|
||||||
import 'webview.dart';
|
import 'webview.dart';
|
||||||
import 'in_app_webview_controller.dart';
|
import 'in_app_webview_controller.dart';
|
||||||
import 'in_app_webview_options.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.
|
///Flutter Widget for adding an **inline native WebView** integrated in the flutter widget tree.
|
||||||
class InAppWebView extends StatefulWidget implements WebView {
|
class InAppWebView extends StatefulWidget implements WebView {
|
||||||
|
@ -38,6 +40,7 @@ class InAppWebView extends StatefulWidget implements WebView {
|
||||||
this.initialData,
|
this.initialData,
|
||||||
this.initialOptions,
|
this.initialOptions,
|
||||||
this.initialUserScripts,
|
this.initialUserScripts,
|
||||||
|
this.pullToRefreshController,
|
||||||
this.contextMenu,
|
this.contextMenu,
|
||||||
this.onWebViewCreated,
|
this.onWebViewCreated,
|
||||||
this.onLoadStart,
|
this.onLoadStart,
|
||||||
|
@ -133,6 +136,9 @@ class InAppWebView extends StatefulWidget implements WebView {
|
||||||
@override
|
@override
|
||||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||||
|
|
||||||
|
@override
|
||||||
|
final PullToRefreshController? pullToRefreshController;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final ContextMenu? contextMenu;
|
final ContextMenu? contextMenu;
|
||||||
|
|
||||||
|
@ -379,6 +385,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
||||||
'initialUserScripts':
|
'initialUserScripts':
|
||||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ??
|
widget.initialUserScripts?.map((e) => e.toMap()).toList() ??
|
||||||
[],
|
[],
|
||||||
|
'pullToRefreshOptions': widget.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||||
},
|
},
|
||||||
creationParamsCodec: const StandardMessageCodec(),
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
)
|
)
|
||||||
|
@ -405,6 +412,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
||||||
'windowId': widget.windowId,
|
'windowId': widget.windowId,
|
||||||
'initialUserScripts':
|
'initialUserScripts':
|
||||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||||
|
'pullToRefreshOptions': widget.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||||
},
|
},
|
||||||
creationParamsCodec: const StandardMessageCodec(),
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
);
|
);
|
||||||
|
@ -425,6 +433,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
||||||
'windowId': widget.windowId,
|
'windowId': widget.windowId,
|
||||||
'initialUserScripts':
|
'initialUserScripts':
|
||||||
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
widget.initialUserScripts?.map((e) => e.toMap()).toList() ?? [],
|
||||||
|
'pullToRefreshOptions': widget.pullToRefreshController?.options.toMap() ?? PullToRefreshOptions(enabled: false).toMap()
|
||||||
},
|
},
|
||||||
creationParamsCodec: const StandardMessageCodec(),
|
creationParamsCodec: const StandardMessageCodec(),
|
||||||
);
|
);
|
||||||
|
@ -445,6 +454,7 @@ class _InAppWebViewState extends State<InAppWebView> {
|
||||||
|
|
||||||
void _onPlatformViewCreated(int id) {
|
void _onPlatformViewCreated(int id) {
|
||||||
_controller = InAppWebViewController(id, widget);
|
_controller = InAppWebViewController(id, widget);
|
||||||
|
widget.pullToRefreshController?.initMethodChannel(id);
|
||||||
if (widget.onWebViewCreated != null) {
|
if (widget.onWebViewCreated != null) {
|
||||||
widget.onWebViewCreated!(_controller);
|
widget.onWebViewCreated!(_controller);
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,15 +52,9 @@ class InAppWebViewController {
|
||||||
HashMap<String, JavaScriptHandlerCallback>();
|
HashMap<String, JavaScriptHandlerCallback>();
|
||||||
List<UserScript> _userScripts = [];
|
List<UserScript> _userScripts = [];
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
bool _isOpened = false;
|
|
||||||
|
|
||||||
// ignore: unused_field
|
// ignore: unused_field
|
||||||
dynamic _id;
|
dynamic _id;
|
||||||
|
|
||||||
// ignore: unused_field
|
|
||||||
String? _inAppBrowserUuid;
|
|
||||||
|
|
||||||
InAppBrowser? _inAppBrowser;
|
InAppBrowser? _inAppBrowser;
|
||||||
|
|
||||||
///Android controller that contains only android-specific methods
|
///Android controller that contains only android-specific methods
|
||||||
|
@ -84,11 +78,9 @@ class InAppWebViewController {
|
||||||
}
|
}
|
||||||
|
|
||||||
InAppWebViewController.fromInAppBrowser(
|
InAppWebViewController.fromInAppBrowser(
|
||||||
String uuid,
|
|
||||||
MethodChannel channel,
|
MethodChannel channel,
|
||||||
InAppBrowser inAppBrowser,
|
InAppBrowser inAppBrowser,
|
||||||
UnmodifiableListView<UserScript>? initialUserScripts) {
|
UnmodifiableListView<UserScript>? initialUserScripts) {
|
||||||
this._inAppBrowserUuid = uuid;
|
|
||||||
this._channel = channel;
|
this._channel = channel;
|
||||||
this._inAppBrowser = inAppBrowser;
|
this._inAppBrowser = inAppBrowser;
|
||||||
this._userScripts =
|
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`.
|
///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),
|
///**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`.
|
///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:
|
///Methods affected:
|
||||||
|
|
|
@ -3,5 +3,6 @@ export 'in_app_webview.dart';
|
||||||
export 'in_app_webview_controller.dart';
|
export 'in_app_webview_controller.dart';
|
||||||
export 'in_app_webview_options.dart';
|
export 'in_app_webview_options.dart';
|
||||||
export 'headless_in_app_webview.dart';
|
export 'headless_in_app_webview.dart';
|
||||||
|
export '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||||
export 'android/main.dart';
|
export 'android/main.dart';
|
||||||
export 'ios/main.dart';
|
export 'ios/main.dart';
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
import 'dart:collection';
|
import 'dart:collection';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import '../pull_to_refresh/pull_to_refresh_controller.dart';
|
||||||
|
|
||||||
import '../context_menu.dart';
|
import '../context_menu.dart';
|
||||||
import '../types.dart';
|
import '../types.dart';
|
||||||
|
|
||||||
|
@ -642,6 +644,9 @@ abstract class WebView {
|
||||||
///This is a limitation of the native iOS WebKit APIs.
|
///This is a limitation of the native iOS WebKit APIs.
|
||||||
final UnmodifiableListView<UserScript>? initialUserScripts;
|
final UnmodifiableListView<UserScript>? initialUserScripts;
|
||||||
|
|
||||||
|
///Represents the pull-to-refresh feature controller.
|
||||||
|
final PullToRefreshController? pullToRefreshController;
|
||||||
|
|
||||||
WebView(
|
WebView(
|
||||||
{this.windowId,
|
{this.windowId,
|
||||||
this.onWebViewCreated,
|
this.onWebViewCreated,
|
||||||
|
@ -701,5 +706,6 @@ abstract class WebView {
|
||||||
this.initialData,
|
this.initialData,
|
||||||
this.initialOptions,
|
this.initialOptions,
|
||||||
this.contextMenu,
|
this.contextMenu,
|
||||||
this.initialUserScripts});
|
this.initialUserScripts,
|
||||||
|
this.pullToRefreshController});
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,3 +14,4 @@ export 'in_app_localhost_server.dart';
|
||||||
export 'content_blocker.dart';
|
export 'content_blocker.dart';
|
||||||
export 'http_auth_credentials_database.dart';
|
export 'http_auth_credentials_database.dart';
|
||||||
export 'context_menu.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:io';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
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 'http_auth_credentials_database.dart';
|
||||||
import 'cookie_manager.dart';
|
import 'cookie_manager.dart';
|
||||||
import 'web_storage/web_storage.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.
|
///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();
|
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
|
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.
|
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
|
homepage: https://github.com/pichillilorenzo/flutter_inappwebview
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
|
|
Loading…
Reference in New Issue