Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 118 additions & 6 deletions packages/webview_flutter/lib/platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,35 @@ import 'package:flutter/widgets.dart';

import 'webview_flutter.dart';

/// Interface for callbacks made by [WebViewPlatform].
///
/// The webview plugin implements this class, and passes an instance to the [WebViewPlatform].
/// [WebViewPlatform] is notifying this handler on events that happened on the platform's webview.
abstract class WebViewPlatformCallbacksHandler {
/// Invoked by [WebViewPlatform] when a JavaScript channel message is received.
void onJavaScriptChannelMessage(String channel, String message);

/// Invoked by [WebViewPlatform] when a navigation request is pending.
///
/// If true is returned the navigation is allowed, otherwise it is blocked.
bool onNavigationRequest({String url, bool isForMainFrame});

/// Invoked by [WebViewPlatform] when a page has finished loading.
void onPageFinished(String url);
}

/// Interface for talking to the webview's platform implementation.
///
/// An instance implementing this interface is passed to the `onWebViewPlatformCreated` callback that is
/// passed to [WebViewPlatformBuilder#onWebViewPlatformCreated].
abstract class WebViewPlatform {
/// Creates a new WebViewPlatform.
///
/// Callbacks made by the WebView will be delegated to `handler`.
///
/// The `handler` parameter must not be null.
WebViewPlatform(WebViewPlatformCallbacksHandler handler);

/// Loads the specified URL.
///
/// If `headers` is not null and the URL is an HTTP URL, the key value paris in `headers` will
Expand All @@ -40,12 +64,94 @@ abstract class WebViewPlatform {
"WebView updateSettings is not implemented on the current platform");
}

// As the PR currently focus about the wiring I've only moved loadUrl to the new way, so
// the discussion is more focused.
// In this temporary state WebViewController still uses a method channel directly for all other
// method calls so we need to expose the webview ID.
// TODO(amirh): remove this before publishing this package.
int get id;
/// Accessor to the current URL that the WebView is displaying.
///
/// If no URL was ever loaded, returns `null`.
Future<String> currentUrl() {
throw UnimplementedError(
"WebView currentUrl is not implemented on the current platform");
}

/// Checks whether there's a back history item.
Future<bool> canGoBack() {
throw UnimplementedError(
"WebView canGoBack is not implemented on the current platform");
}

/// Checks whether there's a forward history item.
Future<bool> canGoForward() {
throw UnimplementedError(
"WebView canGoForward is not implemented on the current platform");
}

/// Goes back in the history of this WebView.
///
/// If there is no back history item this is a no-op.
Future<void> goBack() {
throw UnimplementedError(
"WebView goBack is not implemented on the current platform");
}

/// Goes forward in the history of this WebView.
///
/// If there is no forward history item this is a no-op.
Future<void> goForward() {
throw UnimplementedError(
"WebView goForward is not implemented on the current platform");
}

/// Reloads the current URL.
Future<void> reload() {
throw UnimplementedError(
"WebView reload is not implemented on the current platform");
}

/// Clears all caches used by the [WebView].
///
/// The following caches are cleared:
/// 1. Browser HTTP Cache.
/// 2. [Cache API](https://developers.google.com/web/fundamentals/instant-and-offline/web-storage/cache-api) caches.
/// These are not yet supported in iOS WkWebView. Service workers tend to use this cache.
/// 3. Application cache.
/// 4. Local Storage.
Future<void> clearCache() {
throw UnimplementedError(
"WebView clearCache is not implemented on the current platform");
}

/// Evaluates a JavaScript expression in the context of the current page.
///
/// The Future completes with an error if a JavaScript error occurred, or if the type of the
/// evaluated expression is not supported(e.g on iOS not all non primitive type can be evaluated).
Future<String> evaluateJavascript(String javascriptString) {
throw UnimplementedError(
"WebView evaluateJavascript is not implemented on the current platform");
}

/// Adds new JavaScript channels to the set of enabled channels.
///
/// For each value in this list the platform's webview should make sure that a corresponding
/// property with a postMessage method is set on `window`. For example for a JavaScript channel
/// named `Foo` it should be possible for JavaScript code executing in the webview to do
///
/// ```javascript
/// Foo.postMessage('hello');
/// ```
///
/// See also: [CreationParams.javascriptChannelNames].
Future<void> addJavascriptChannels(Set<String> javascriptChannelNames) {
throw UnimplementedError(
"WebView addJavascriptChannels is not implemented on the current platform");
}

/// Removes JavaScript channel names from the set of enabled channels.
///
/// This disables channels that were previously enabled by [addJavaScriptChannels] or through
/// [CreationParams.javascriptChannelNames].
Future<void> removeJavascriptChannels(Set<String> javascriptChannelNames) {
throw UnimplementedError(
"WebView removeJavascriptChannels is not implemented on the current platform");
}
}

/// Settings for configuring a WebViewPlatform.
Expand Down Expand Up @@ -125,6 +231,9 @@ abstract class WebViewBuilder {
///
/// `creationParams` are the initial parameters used to setup the webview.
///
/// `webViewPlatformHandler` will be used for handling callbacks that are made by the created
/// [WebViewPlatform].
///
/// `onWebViewPlatformCreated` will be invoked after the platform specific [WebViewPlatform]
/// implementation is created with the [WebViewPlatform] instance as a parameter.
///
Expand All @@ -135,12 +244,15 @@ abstract class WebViewBuilder {
/// recognizers on this list.
/// When `gestureRecognizers` is empty or null, the web view will only handle pointer events for gestures that
/// were not claimed by any other gesture recognizer.
///
/// `webViewPlatformHandler` must not be null.
Widget build({
BuildContext context,
// TODO(amirh): convert this to be the actual parameters.
// I'm starting without it as the PR is starting to become pretty big.
// I'll followup with the conversion PR.
CreationParams creationParams,
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
});
Expand Down
5 changes: 4 additions & 1 deletion packages/webview_flutter/lib/src/webview_android.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ class AndroidWebViewBuilder implements WebViewBuilder {
Widget build({
BuildContext context,
CreationParams creationParams,
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
}) {
assert(webViewPlatformCallbacksHandler != null);
return GestureDetector(
// We prevent text selection by intercepting the long press event.
// This is a temporary stop gap due to issues with text selection on Android:
Expand All @@ -39,7 +41,8 @@ class AndroidWebViewBuilder implements WebViewBuilder {
if (onWebViewPlatformCreated == null) {
return;
}
onWebViewPlatformCreated(MethodChannelWebViewPlatform(id));
onWebViewPlatformCreated(MethodChannelWebViewPlatform(
id, webViewPlatformCallbacksHandler));
},
gestureRecognizers: gestureRecognizers,
// WebView content is not affected by the Android view's layout direction,
Expand Down
4 changes: 3 additions & 1 deletion packages/webview_flutter/lib/src/webview_cupertino.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class CupertinoWebViewBuilder implements WebViewBuilder {
Widget build({
BuildContext context,
CreationParams creationParams,
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
}) {
Expand All @@ -29,7 +30,8 @@ class CupertinoWebViewBuilder implements WebViewBuilder {
if (onWebViewPlatformCreated == null) {
return;
}
onWebViewPlatformCreated(MethodChannelWebViewPlatform(id));
onWebViewPlatformCreated(
MethodChannelWebViewPlatform(id, webViewPlatformCallbacksHandler));
},
gestureRecognizers: gestureRecognizers,
creationParams:
Expand Down
70 changes: 64 additions & 6 deletions packages/webview_flutter/lib/src/webview_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,36 @@ import '../platform_interface.dart';

/// A [WebViewPlatform] that uses a method channel to control the webview.
class MethodChannelWebViewPlatform implements WebViewPlatform {
MethodChannelWebViewPlatform(this._id)
: _channel = MethodChannel('plugins.flutter.io/webview_$_id');
MethodChannelWebViewPlatform(int id, this._platformCallbacksHandler)
: assert(_platformCallbacksHandler != null),
_channel = MethodChannel('plugins.flutter.io/webview_$id') {
_channel.setMethodCallHandler(_onMethodCall);
}

final int _id;
final WebViewPlatformCallbacksHandler _platformCallbacksHandler;

final MethodChannel _channel;

Future<bool> _onMethodCall(MethodCall call) async {
switch (call.method) {
case 'javascriptChannelMessage':
final String channel = call.arguments['channel'];
final String message = call.arguments['message'];
_platformCallbacksHandler.onJavaScriptChannelMessage(channel, message);
return true;
case 'navigationRequest':
return _platformCallbacksHandler.onNavigationRequest(
url: call.arguments['url'],
isForMainFrame: call.arguments['isForMainFrame'],
);
case 'onPageFinished':
_platformCallbacksHandler.onPageFinished(call.arguments['url']);
return null;
}
throw MissingPluginException(
'${call.method} was invoked but has no handler');
}

@override
Future<void> loadUrl(
String url,
Expand All @@ -32,6 +55,27 @@ class MethodChannelWebViewPlatform implements WebViewPlatform {
});
}

@override
Future<String> currentUrl() => _channel.invokeMethod('currentUrl');

@override
Future<bool> canGoBack() => _channel.invokeMethod("canGoBack");

@override
Future<bool> canGoForward() => _channel.invokeMethod("canGoForward");

@override
Future<void> goBack() => _channel.invokeMethod("goBack");

@override
Future<void> goForward() => _channel.invokeMethod("goForward");

@override
Future<void> reload() => _channel.invokeMethod("reload");

@override
Future<void> clearCache() => _channel.invokeMethod("clearCache");

@override
Future<void> updateSettings(WebSettings settings) {
// TODO(amirh): remove this on when the invokeMethod update makes it to stable Flutter.
Expand All @@ -44,6 +88,23 @@ class MethodChannelWebViewPlatform implements WebViewPlatform {
return _channel.invokeMethod('updateSettings', updatesMap);
}

@override
Future<String> evaluateJavascript(String javascriptString) {
return _channel.invokeMethod('evaluateJavascript', javascriptString);
}

@override
Future<void> addJavascriptChannels(Set<String> javascriptChannelNames) {
return _channel.invokeMethod(
'addJavascriptChannels', javascriptChannelNames.toList());
}

@override
Future<void> removeJavascriptChannels(Set<String> javascriptChannelNames) {
return _channel.invokeMethod(
'removeJavascriptChannels', javascriptChannelNames.toList());
}

static Map<String, dynamic> _webSettingsToMap(WebSettings settings) {
final Map<String, dynamic> map = <String, dynamic>{};
void _addIfNonNull(String key, dynamic value) {
Expand Down Expand Up @@ -71,7 +132,4 @@ class MethodChannelWebViewPlatform implements WebViewPlatform {
'javascriptChannelNames': creationParams.javascriptChannelNames.toList(),
};
}

@override
int get id => _id;
}
Loading