Skip to content

Commit

Permalink
v3.2.0, added context menu classes, updated docs, fix #235, fix #337, f…
Browse files Browse the repository at this point in the history
…ix #341
  • Loading branch information
Lorenzo Pichilli committed May 21, 2020
1 parent ecf8d45 commit 5943059
Show file tree
Hide file tree
Showing 34 changed files with 1,326 additions and 337 deletions.
13 changes: 12 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
## 3.1.1
## 3.2.0

- Added `ContextMenu` and `ContextMenuItem` classes [#235](https://github.com/pichillilorenzo/flutter_inappwebview/issues/235)
- Added `onCreateContextMenu`, `onHideContextMenu`, `onContextMenuActionItemClicked` context menu events
- Added `contextMenu` to WebView
- Added `disableContextMenu` WebView option
- Added `getSelectedText`, `getHitTestResult` methods to WebView Controller
- Fixed `Confirmation dialog (onbeforeunload) displayed after popped from webview page` [#337](https://github.com/pichillilorenzo/flutter_inappwebview/issues/337)
- Fixed `CookieManager.setCookie` `expiresDate` option
- Fixed `Scrolling not smooth on iOS` [#341](https://github.com/pichillilorenzo/flutter_inappwebview/issues/341)

### BREAKING CHANGES

- Renamed `LongPressHitTestResult` to `InAppWebViewHitTestResult`.
- Renamed `LongPressHitTestResultType` to `InAppWebViewHitTestResultType`.

## 3.1.0

Expand Down
173 changes: 171 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Also, you need to add `<uses-permission android:name="android.permission.INTERNE

Because of [Flutter AndroidX compatibility](https://flutter.dev/docs/development/packages-and-plugins/androidx-compatibility), the latest version that doesn't use `AndroidX` is `0.6.0`.

Also, note that to use the `InAppWebView` widget on Android, it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html)).

### IMPORTANT Note for iOS

If you are starting a new fresh app, you need to create the Flutter App with `flutter create --androidx -i swift` (see [flutter/flutter#13422 (comment)](https://github.com/flutter/flutter/issues/13422#issuecomment-392133780)), otherwise, you will get this message:
Expand Down Expand Up @@ -67,7 +69,8 @@ First, add `flutter_inappwebview` as a [dependency in your pubspec.yaml file](ht
## Usage

Classes:
- [InAppWebView](#inappwebview-class): Flutter Widget for adding an **inline native WebView** integrated into the flutter widget tree. To use `InAppWebView` class on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` and the value `YES`.
- [InAppWebView](#inappwebview-class): Flutter Widget for adding an **inline native WebView** integrated into the flutter widget tree. To use `InAppWebView` class on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` and the value `YES`. Also, note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html)).
- [ContextMenu](#contextmenu-class): This class represents the WebView context menu.
- [HeadlessInAppWebView](#headlessinappwebview-class): 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.
- [InAppBrowser](#inappbrowser-class): In-App Browser using native WebView.
- [ChromeSafariBrowser](#chromesafaribrowser-class): In-App Browser using [Chrome Custom Tabs](https://developer.android.com/reference/android/support/customtabs/package-summary) on Android / [SFSafariViewController](https://developer.apple.com/documentation/safariservices/sfsafariviewcontroller) on iOS.
Expand Down Expand Up @@ -118,6 +121,8 @@ Keyboard support within webviews is also experimental.

To use `InAppWebView` class on iOS you need to opt-in for the embedded views preview by adding a boolean property to the app's `Info.plist` file, with the key `io.flutter.embedded_views_preview` and the value `YES`.

Also, note that on Android it requires **Android API 20+** (see [AndroidView](https://api.flutter.dev/flutter/widgets/AndroidView-class.html)).

Use `InAppWebViewController` to control the WebView instance.
Example:
```dart
Expand All @@ -133,7 +138,7 @@ Future main() async {
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}z
}
class _MyAppState extends State<MyApp> {
Expand Down Expand Up @@ -296,6 +301,8 @@ Screenshots:
* `resumeTimers`: On Android, it resumes all layout, parsing, and JavaScript timers for all WebViews. This will resume dispatching all timers. On iOS, it resumes all layout, parsing, and JavaScript timers to just this WebView.
* `printCurrentPage`: Prints the current page.
* `getScale`: Gets the current scale of this WebView.
* `getSelectedText`: Gets the selected text.
* `getHitTestResult`: Gets the hit result for hitting an HTML elements.
* `static getDefaultUserAgent`: Gets the default user agent.

##### `InAppWebViewController` Android-specific methods
Expand All @@ -315,6 +322,7 @@ Android-specific methods can be called using the `InAppWebViewController.android

iOS-specific methods can be called using the `InAppWebViewController.ios` attribute.

* `hasOnlySecureContent`: A Boolean value indicating whether all resources on the page have been loaded over securely encrypted connections.
* `reloadFromOrigin`: Reloads the current page, performing end-to-end revalidation using cache-validating conditionals if possible.

##### About the JavaScript handler
Expand Down Expand Up @@ -389,6 +397,7 @@ Instead, on the `onLoadStop` WebView event, you can use `callHandler` directly:
* `transparentBackground`: Set to `true` to make the background of the WebView transparent. If your app has a dark theme, this can prevent a white flash on initialization. The default value is `false`.
* `disableVerticalScroll`: Set to `true` to disable vertical scroll. The default value is `false`.
* `disableHorizontalScroll`: Set to `true` to disable horizontal scroll. The default value is `false`.
* `disableContextMenu`: Set to `true` to disable context menu. The default value is `false`.

##### `InAppWebView` Android-specific options

Expand Down Expand Up @@ -499,6 +508,166 @@ Event names that starts with `android` or `ios` are events platform-specific.
* `iosOnDidCommit`: Called when the web view begins to receive web content (available only on iOS).
* `iosOnDidReceiveServerRedirectForProvisionalNavigation`: Called when a web view receives a server redirect (available only on iOS).

### `ContextMenu` class

Class that represents the WebView context menu. It used by `WebView.contextMenu`.

`ContextMenu.menuItems` contains the list of the custom `ContextMenuItem`.

**NOTE**: To make it work properly on Android, JavaScript should be enabled!

Example:
```dart
import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(new MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
InAppWebViewController webView;
ContextMenu contextMenu;
String url = "";
double progress = 0;
@override
void initState() {
super.initState();
contextMenu = ContextMenu(
onCreateContextMenu: (hitTestResult) async {
print("onCreateContextMenu");
print(hitTestResult.extra);
print(await webView.getSelectedText());
},
onHideContextMenu: () {
print("onHideContextMenu");
},
onContextMenuActionItemClicked: (contextMenuItemClicked) {
var id = (Platform.isAndroid) ? contextMenuItemClicked.androidId : contextMenuItemClicked.iosId;
print("onContextMenuActionItemClicked: " + id.toString() + " " + contextMenuItemClicked.title);
}
);
contextMenu.menuItems = [
ContextMenuItem(androidId: 1, iosId: "1", title: "Special", action: () async {
print("Menu item Special clicked!");
})
];
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('InAppWebView Example'),
),
body: Container(
child: Column(children: <Widget>[
Container(
padding: EdgeInsets.all(20.0),
child: Text(
"CURRENT URL\n${(url.length > 50) ? url.substring(0, 50) + "..." : url}"),
),
Container(
padding: EdgeInsets.all(10.0),
child: progress < 1.0
? LinearProgressIndicator(value: progress)
: Container()),
Expanded(
child: Container(
margin: const EdgeInsets.all(10.0),
decoration:
BoxDecoration(border: Border.all(color: Colors.blueAccent)),
child: InAppWebView(
initialUrl: "https://flutter.dev/",
contextMenu: contextMenu,
initialHeaders: {},
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
debuggingEnabled: true,
)
),
onWebViewCreated: (InAppWebViewController controller) {
webView = controller;
},
onLoadStart: (InAppWebViewController controller, String url) {
setState(() {
this.url = url;
});
},
onLoadStop: (InAppWebViewController controller, String url) async {
setState(() {
this.url = url;
});
},
onProgressChanged: (InAppWebViewController controller, int progress) {
setState(() {
this.progress = progress / 100;
});
},
),
),
),
ButtonBar(
alignment: MainAxisAlignment.center,
children: <Widget>[
RaisedButton(
child: Icon(Icons.arrow_back),
onPressed: () {
if (webView != null) {
webView.goBack();
}
},
),
RaisedButton(
child: Icon(Icons.arrow_forward),
onPressed: () {
if (webView != null) {
webView.goForward();
}
},
),
RaisedButton(
child: Icon(Icons.refresh),
onPressed: () {
if (webView != null) {
webView.reload();
}
},
),
],
),
])),
),
);
}
}
```

### `ContextMenu` Events

* `onCreateContextMenu`: Event fired when the context menu for this WebView is being built.
* `onHideContextMenu`: Event fired when the context menu for this WebView is being hidden.
* `onContextMenuActionItemClicked`: Event fired when a context menu item has been clicked.

### `HeadlessInAppWebView` class

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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result resul
myIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
myIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Shared.activity.startActivity(myIntent);
result.success(true);
break;
default:
result.notImplemented();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,21 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result resul
{
String url = (String) call.argument("url");
HashMap<String, Object> options = (HashMap<String, Object>) call.argument("options");
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
String uuidFallback = (String) call.argument("uuidFallback");
Map<String, String> headersFallback = (Map<String, String>) call.argument("headersFallback");
HashMap<String, Object> optionsFallback = (HashMap<String, Object>) call.argument("optionsFallback");
List<HashMap<String, Object>> menuItemList = (List<HashMap<String, Object>>) call.argument("menuItemList");
open(activity, uuid, url, options, uuidFallback, headersFallback, optionsFallback, menuItemList, result);
HashMap<String, Object> contextMenuFallback = (HashMap<String, Object>) call.argument("contextMenuFallback");
open(activity, uuid, url, options, menuItemList, uuidFallback, headersFallback, optionsFallback, contextMenuFallback, result);
}
break;
default:
result.notImplemented();
}
}

public void open(Activity activity, String uuid, String url, HashMap<String, Object> options, String uuidFallback,
Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, List<HashMap<String, Object>> menuItemList, MethodChannel.Result result) {
public void open(Activity activity, String uuid, String url, HashMap<String, Object> options, List<HashMap<String, Object>> menuItemList, String uuidFallback,
Map<String, String> headersFallback, HashMap<String, Object> optionsFallback, HashMap<String, Object> contextMenuFallback, MethodChannel.Result result) {

Intent intent = null;
Bundle extras = new Bundle();
Expand All @@ -62,9 +63,11 @@ public void open(Activity activity, String uuid, String url, HashMap<String, Obj
extras.putBoolean("isData", false);
extras.putString("uuid", uuid);
extras.putSerializable("options", options);
extras.putSerializable("headers", (Serializable) headersFallback);
extras.putSerializable("menuItemList", (Serializable) menuItemList);

extras.putSerializable("headers", (Serializable) headersFallback);
extras.putSerializable("contextMenu", (Serializable) contextMenuFallback);

if (CustomTabActivityHelper.isAvailable(activity)) {
intent = new Intent(activity, ChromeCustomTabsActivity.class);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,15 @@ protected void onCreate(Bundle savedInstanceState) {
fromActivity = b.getString("fromActivity");

HashMap<String, Object> optionsMap = (HashMap<String, Object>) b.getSerializable("options");
HashMap<String, Object> contextMenu = (HashMap<String, Object>) b.getSerializable("contextMenu");

options = new InAppBrowserOptions();
options.parse(optionsMap);

InAppWebViewOptions webViewOptions = new InAppWebViewOptions();
webViewOptions.parse(optionsMap);
webView.options = webViewOptions;
webView.contextMenu = contextMenu;

actionBar = getSupportActionBar();

Expand Down Expand Up @@ -223,7 +225,7 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result resul
result.success(isHidden);
break;
case "takeScreenshot":
result.success(takeScreenshot());
takeScreenshot(result);
break;
case "setOptions":
{
Expand Down Expand Up @@ -330,6 +332,16 @@ public void onMethodCall(final MethodCall call, final MethodChannel.Result resul
case "getScale":
result.success(getScale());
break;
case "getSelectedText":
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getSelectedText(result);
} else {
result.success(null);
}
break;
case "getHitTestResult":
result.success(getHitTestResult());
break;
default:
result.notImplemented();
}
Expand Down Expand Up @@ -619,24 +631,11 @@ public void closeButtonClicked(MenuItem item) {
close(null);
}

public byte[] takeScreenshot() {
if (webView != null) {
Picture picture = webView.capturePicture();
Bitmap b = Bitmap.createBitmap( webView.getWidth(),
webView.getHeight(), Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);

picture.draw(c);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
b.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
return byteArrayOutputStream.toByteArray();
}
return null;
public void takeScreenshot(MethodChannel.Result result) {
if (webView != null)
webView.takeScreenshot(result);
else
result.success(null);
}

public void setOptions(InAppBrowserOptions newOptions, HashMap<String, Object> newOptionsMap) {
Expand Down Expand Up @@ -843,6 +842,25 @@ public Float getScale() {
return null;
}

@RequiresApi(api = Build.VERSION_CODES.KITKAT)
public void getSelectedText(MethodChannel.Result result) {
if (webView != null)
webView.getSelectedText(result);
else
result.success(null);
}

public Map<String, Object> getHitTestResult() {
if (webView != null) {
WebView.HitTestResult hitTestResult = webView.getHitTestResult();
Map<String, Object> obj = new HashMap<>();
obj.put("type", hitTestResult.getType());
obj.put("extra", hitTestResult.getExtra());
return obj;
}
return null;
}

public void dispose() {
channel.setMethodCallHandler(null);
if (webView != null) {
Expand Down
Loading

0 comments on commit 5943059

Please sign in to comment.