Skip to content
This repository has been archived by the owner on Mar 14, 2023. It is now read-only.

Commit

Permalink
Bump version. Breaking changes, please read #26 and #27
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianflutur committed Aug 17, 2021
1 parent 8101fbc commit 4e625e8
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 62 deletions.
25 changes: 25 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,28 @@
## 0.2.0

- Deprecated pedantic. Adopted lint instead.
- Abstracted the controller and the widget. Soon it will be possible to add multiple implementations, such as windows, macOs or linux.
- Renamed SourceType enum and AutoMediaPlaybackPolicy enums acording to lint rules (camelCase instead of SCREAM_CASE)
- (web) Moved huge part of JS logic to Dart = better control over what happens there (might move it all to Dart soon)
- (web) Added the option to supply your own list of BypassProxy objects. This means anyone can now spin up their own proxy server and add it to the list, if they don't want to run on the default public ones.
- (web) Implemented navigationDelegate
- (web) Fixed onPageStarted and onPageFinished callbacks. Now they provide the correct information.
- (web) Implemented missing features from WebviewXController which were available on mobile:

```
Future getScrollX();
Future getScrollY();
Future scrollBy(int x, int y);
Future scrollTo(int x, int y);
Future<String?> getTitle();
Future clearCache();
```

- (mobile) Fixed sourceType desync
- (mobile) Fixed URI data: source messing up sometimes due to the encoding
- Update documentation
- Update dependencies

## 0.1.0

- Migrated to null safety
Expand Down
67 changes: 38 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,13 @@ A feature-rich cross-platform webview using [webview_flutter](https://pub.dev/pa

## Basic usage

1. Create a `WebViewXController` inside your stateful widget
### **1.** Create a `WebViewXController` inside your stateful widget

```dart
late WebViewXController webviewController;
```

2. Add the WebViewX widget inside the build method, and set the `onWebViewCreated` callback in order to retrieve the controller when the webview is initialized
### **2.** Add the WebViewX widget inside the build method, and set the `onWebViewCreated` callback in order to retrieve the controller when the webview is initialized

```dart
WebViewX(
Expand All @@ -85,14 +85,14 @@ WebViewX(
If you need to add other widgets on top of the webview (e.g. inside a Stack widget), you _**MUST**_ wrap those widgets with a **WebViewAware** widget.
This does nothing on mobile, but on web it allows widgets on top to intercept gestures. Otherwise, those widgets may not be clickable and/or the iframe will behave weird (unexpected refresh/reload - this is a well known issue).

Also, as a side note: If you happen to add widgets on top of the webview, wrap them and then you notice that the iframe still reloads unexpectedly, you should check if there are other widgets that sit on top without being noticed, or try to wrap InkWell, GestureRecognizer or Button widgets to see which one causes the problem.
Also, if you add widgets on top of the webview, wrap them and then you notice that the iframe still reloads unexpectedly, you should check if there are other widgets that sit on top without being noticed, or try to wrap InkWell, GestureRecognizer or Button widgets to see which one causes the problem.

3. Interact with the controller (run the [example app](https://github.com/adrianflutur/webviewx/tree/main/example) to see how does it work)
### **3.** Interact with the controller (run the [example app](https://github.com/adrianflutur/webviewx/tree/main/example) to check out some use cases)

```dart
webviewController.loadContent(
'https://flutter.dev',
SourceType.URL,
SourceType.url,
);
webviewController.goBack();
Expand All @@ -105,15 +105,15 @@ webviewController.goForward();

## Features

Note: For instructions on how to use theese features, please see each one's documentation, located inside it's coresponding class.
Note: For more detailed information about things such as `EmbeddedJsContent`, please visit each own's `.dart` file from the `utils` folder.

- ### Widget properties
- ## Widget properties

| Feature | Details |
| ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `String` initialContent | Initial webview content |
| `SourceType` initialSourceType | Initial webview content type (`URL, URL_BYPASS or HTML`) |
| `String?` userAgent | User agent ( [issues on web](#known-issues-and-todos) ) |
| `SourceType` initialSourceType | Initial webview content type (`url, urlBypass or html`) |
| `String?` userAgent | User agent |
| `double?` width | Widget's width (if null, it takes all available space) |
| `double?` height | Widget's height (if null, it takes all available space) |
| `Function(WebViewXController controller)?` onWebViewCreated | Callback that gets executed when the webview has initialized |
Expand All @@ -124,13 +124,14 @@ Note: For instructions on how to use theese features, please see each one's docu
| `AutoMediaPlaybackPolicy` initialMediaPlaybackPolicy | This specifies if media content should be allowed to autoplay when initialized (i.e when the page is loaded) |
| `void Function(String src)?` onPageStarted | Callback that gets executed when a page starts loading (e.g. after you change the content) |
| `void Function(String src)?` onPageFinished | Callback that gets executed when a page finishes loading |
| `NavigationDelegate?` navigationDelegate | Callback that, if not null, gets executed when the user clicks something in the webview (on Web it only works for `SourceType.urlBypass`, for now) |
| `void Function(WebResourceError error)?` onWebResourceError | Callback that gets executed when there is an error when loading resources ( [issues on web](#known-issues-and-todos) ) |
| `WebSpecificParams` webSpecificParams | This is an object that contains web-specific options. Theese are not available on mobile (_yet_) |
| `MobileSpecificParams` mobileSpecificParams | This is an object that contains mobile-specific options. Theese are not available on web (_yet_) |

---

- ### Controller properties
- ## Controller properties

| Feature | Usage |
| --------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
Expand All @@ -139,17 +140,23 @@ Note: For instructions on how to use theese features, please see each one's docu
| Load URL that doesnt allow iframe embedding, with headers | webviewController.`loadContent(URL, SourceType.URL_BYPASS, headers: {'x-something': 'value'})` |
| Load HTML from string | webviewController.`loadContent(HTML, SourceType.HTML)` |
| Load HTML from assets | webviewController.`loadContent(HTML, SourceType.HTML, fromAssets: true)` |
| Check if you can go back in history | `await` webviewController.`canGoBack()` |
| Go back in history | `await` webviewController.`goBack()` |
| Check if you can go forward in history | `await` webviewController.`canGoForward()` |
| Go forward in history | `await` webviewController.`goForward()` |
| Check if you can go back in history | webviewController.`canGoBack()` |
| Go back in history | webviewController.`goBack()` |
| Check if you can go forward in history | webviewController.`canGoForward()` |
| Go forward in history | webviewController.`goForward()` |
| Reload current content | webviewController.`reload()` |
| Check if all gestures are ignored | webviewController.`ignoringAllGestures` |
| Set ignore all gestures | webviewController.`setIgnoreAllGestures(value)` |
| Evaluate "raw" javascript code | `await` webviewController.`evalRawJavascript(JS)` |
| Evaluate "raw" javascript code in global context ("page") | `await` webviewController.`evalRawJavascript(JS, inGlobalContext: true)` |
| Call a JS method | `await` webviewController.`callJsMethod(METHOD_NAME, PARAMS_LIST)` |
| Retrieve webview's content | `await` webviewController.`getContent()` |
| Evaluate "raw" javascript code | webviewController.`evalRawJavascript(JS)` |
| Evaluate "raw" javascript code in global context ("page") | webviewController.`evalRawJavascript(JS, inGlobalContext: true)` |
| Call a JS method | webviewController.`callJsMethod(METHOD_NAME, PARAMS_LIST)` |
| Retrieve webview's content | webviewController.`getContent()` |
| Get scroll position on X axis | webviewController.`getScrollX()` |
| Get scroll position on Y axis | webviewController.`getScrollY()` |
| Scrolls by `x` on X axis and by `y` on Y axis | webviewController.`scrollBy(int x, int y)` |
| Scrolls exactly to the position `(x, y)` | webviewController.`scrollTo(int x, int y)` |
| Retrieves the inner page title | webviewController.`getTitle()` |
| Clears cache | webviewController.`clearCache()` |

---

Expand Down Expand Up @@ -177,31 +184,33 @@ While this package aims to put together the best of both worlds, there are diffe

for building.

- Behaviour when loading content
- Diferences between Web and Mobile behaviour:

To make the web version (iframe) work as it is, I had to use this [x-frame bypass](https://github.com/niutech/x-frame-bypass) in order to make a request to a pre-defined CORS proxy, which removes the headers that block iframe embeddings.
See issues/

This might seem like a hack, and it really is, but I couldn't find any other way to make the iframe behave similar to the mobile webview (which is some kind of an actual browser, that's why everything works there by default).
- About content loading on Web

To make the web version (iframe) work as it is, I had to use some of the code from [x-frame bypass](https://github.com/niutech/x-frame-bypass) in order to make a request to a CORS proxy, which removes the headers that block iframe embeddings.

Also, it might not work on Safari, as stated [here](https://github.com/niutech/x-frame-bypass).
This might seem like a hack, and it really is, but I couldn't find any other way to make the iframe behave similar to the mobile webview (which is some kind of an actual browser, that's why everything works there by default).

- Web navigation
- About Web navigation

On web, the history navigation stack is built from scratch because I couldn't handle iframe's internal history the right way.

---

## Known issues and TODOs

- [ ] On web, user-agent and headers only work when using `SourceType.URL_BYPASS`, and they only have effect the first time being used (`view/web.dart`)
- [ x ] On web, user-agent and headers only work when using `SourceType.urlBypass`, and they only have effect the first time being used (`view/web.dart`)

- [ ] On web, it should be possible to send any errors caught when loading an `URL_BYPASS` to a dart callback, which will then be sent through the `onWebResourceError` callback, just like on the mobile version (`utils/x_frame_options_bypass.dart`)
- [ x ] On web, it should be possible to send any errors caught when loading an `urlBypass` to a dart callback, which will then be sent through the `onWebResourceError` callback, just like on the mobile version (`utils/x_frame_options_bypass.dart`)

- [ ] On web, it should be possible to add a custom proxy list without the js null-checking mess (`utils/x_frame_options_bypass.dart`)
- [ x ] On web, it should be possible to add a custom proxy list without the js null-checking mess (`utils/x_frame_options_bypass.dart`)

- [ ] Eventually (if possible), most if not all properties from `WebSpecificParams` and `MobileSpecificParams` should merge and theese two objects may disappear
- [ ? ] Eventually (if possible), most if not all properties from `WebSpecificParams` and `MobileSpecificParams` should merge and theese two objects may disappear

- [ ] On mobile, the controller's value's source type becomes out of sync when moving back and forth in history. This happens because the url change is not yet intercepted and set the model accordingly (shouldn't be hard to fix).
- [ x ] On mobile, the controller's value's source type becomes out of sync when moving back and forth in history. This happens because the url change is not yet intercepted and set the model accordingly (shouldn't be hard to fix).

- [ ] On mobile, the controller's callJsMethod doesnt throw an error if the operation failed. Instead it only shows the error in console.

Expand All @@ -224,4 +233,4 @@ This package wouldn't be possible without the following:

## License

## [MIT](https://github.com/adrianflutur/webviewx/blob/master/LICENSE)
[MIT](https://github.com/adrianflutur/webviewx/blob/master/LICENSE)
12 changes: 8 additions & 4 deletions example/lib/webview_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ class _WebViewXPageState extends State<WebViewXPage> {
initialContent: initialContent,
initialSourceType: SourceType.html,
onWebViewCreated: (controller) => webviewController = controller,
onPageStarted: (src) => debugPrint('A new page has started loading: $src\n'),
onPageFinished: (src) => debugPrint('The page has finished loading: $src\n'),
onPageStarted: (src) =>
debugPrint('A new page has started loading: $src\n'),
onPageFinished: (src) =>
debugPrint('The page has finished loading: $src\n'),
jsContent: const {
EmbeddedJsContent(
js: "function testPlatformIndependentMethod() { console.log('Hi from JS') }",
Expand Down Expand Up @@ -197,7 +199,8 @@ class _WebViewXPageState extends State<WebViewXPage> {

Future<void> _callPlatformSpecificJsMethod() async {
try {
await webviewController.callJsMethod('testPlatformSpecificMethod', ['Hi']);
await webviewController
.callJsMethod('testPlatformSpecificMethod', ['Hi']);
} catch (e) {
showAlertDialog(
executeJsErrorMessage,
Expand Down Expand Up @@ -285,7 +288,8 @@ class _WebViewXPageState extends State<WebViewXPage> {
),
buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
createButton(
text: 'Call platform specific Js method, that calls back a Dart function',
text:
'Call platform specific Js method, that calls back a Dart function',
onTap: _callPlatformSpecificJsMethod,
),
buildSpace(direction: Axis.vertical, flex: false, amount: 20.0),
Expand Down
4 changes: 2 additions & 2 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,14 @@ packages:
name: webview_flutter
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8"
version: "2.0.12"
webviewx:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.1.0"
version: "0.2.0"
sdks:
dart: ">=2.12.0 <3.0.0"
flutter: ">=2.0.0"
3 changes: 2 additions & 1 deletion lib/src/controller/impl/mobile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class WebViewXController extends ChangeNotifier
/// [SourceType] is [SourceType.urlBypass], which means it should
/// use the bypass to fetch the web page content.
@override
bool get isCurrentContentURLBypass => value.sourceType == SourceType.urlBypass;
bool get isCurrentContentURLBypass =>
value.sourceType == SourceType.urlBypass;

/// Set webview content to the specified `content`.
/// Example: https://flutter.dev/
Expand Down
3 changes: 2 additions & 1 deletion lib/src/controller/impl/web.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ class WebViewXController extends ChangeNotifier
/// [SourceType] is [SourceType.urlBypass], which means it should
/// use the proxy bypass to fetch the web page content.
@override
bool get isCurrentContentURLBypass => value.sourceType == SourceType.urlBypass;
bool get isCurrentContentURLBypass =>
value.sourceType == SourceType.urlBypass;

/// Set webview content to the specified `content`.
/// Example: https://flutter.dev/
Expand Down
Loading

0 comments on commit 4e625e8

Please sign in to comment.