Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

[webview_flutter] Migrate to nnbd #3327

Merged
merged 4 commits into from
Dec 15, 2020
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
4 changes: 4 additions & 0 deletions packages/webview_flutter/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.0.0-nullsafety

* Migration to null-safety.

## 1.0.8

* Update Flutter SDK constraint.
Expand Down
64 changes: 36 additions & 28 deletions packages/webview_flutter/lib/platform_interface.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ abstract class WebViewPlatformCallbacksHandler {
/// Invoked by [WebViewPlatformController] when a navigation request is pending.
///
/// If true is returned the navigation is allowed, otherwise it is blocked.
FutureOr<bool> onNavigationRequest({String url, bool isForMainFrame});
FutureOr<bool> onNavigationRequest(
{required String url, required bool isForMainFrame});

/// Invoked by [WebViewPlatformController] when a page has started loading.
void onPageStarted(String url);
Expand Down Expand Up @@ -103,8 +104,8 @@ class WebResourceError {
/// A user should not need to instantiate this class, but will receive one in
/// [WebResourceErrorCallback].
WebResourceError({
@required this.errorCode,
@required this.description,
required this.errorCode,
required this.description,
this.domain,
this.errorType,
this.failingUrl,
Expand All @@ -131,21 +132,21 @@ class WebResourceError {
/// in Objective-C. See
/// https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ErrorHandlingCocoa/ErrorObjectsDomains/ErrorObjectsDomains.html
/// for more information on error handling on iOS.
final String domain;
final String? domain;

/// Description of the error that can be used to communicate the problem to the user.
final String description;

/// The type this error can be categorized as.
///
/// This will never be `null` on Android, but can be `null` on iOS.
final WebResourceErrorType errorType;
final WebResourceErrorType? errorType;

/// Gets the URL for which the resource request was made.
///
/// This value is not provided on iOS. Alternatively, you can keep track of
/// the last values provided to [WebViewPlatformController.loadUrl].
final String failingUrl;
final String? failingUrl;
}

/// Interface for talking to the webview's platform implementation.
Expand Down Expand Up @@ -176,7 +177,7 @@ abstract class WebViewPlatformController {
/// Throws an ArgumentError if `url` is not a valid URL string.
Future<void> loadUrl(
String url,
Map<String, String> headers,
Map<String, String>? headers,
) {
throw UnimplementedError(
"WebView loadUrl is not implemented on the current platform");
Expand All @@ -194,7 +195,7 @@ abstract class WebViewPlatformController {
/// Accessor to the current URL that the WebView is displaying.
///
/// If no URL was ever loaded, returns `null`.
Future<String> currentUrl() {
Future<String?> currentUrl() {
throw UnimplementedError(
"WebView currentUrl is not implemented on the current platform");
}
Expand Down Expand Up @@ -281,7 +282,7 @@ abstract class WebViewPlatformController {
}

/// Returns the title of the currently loaded page.
Future<String> getTitle() {
Future<String?> getTitle() {
throw UnimplementedError(
"WebView getTitle is not implemented on the current platform");
}
Expand Down Expand Up @@ -337,7 +338,7 @@ class WebSetting<T> {
: _value = value,
isPresent = true;

final T _value;
final T? _value;

/// The setting's value.
///
Expand All @@ -347,7 +348,14 @@ class WebSetting<T> {
throw StateError('Cannot access a value of an absent WebSetting');
}
assert(isPresent);
return _value;
// The intention of this getter is to return T whether it is nullable or
// not whereas _value is of type T? since _value can be null even when
// T is not nullable (when isPresent == false).
//
// We promote _value to T using `as T` instead of `!` operator to handle
// the case when _value is legitimately null (and T is a nullable type).
// `!` operator would always throw if _value is null.
return _value as T;
}

/// True when this web setting instance contains a value.
Expand All @@ -358,7 +366,7 @@ class WebSetting<T> {
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
final WebSetting<T> typedOther = other;
final WebSetting<T> typedOther = other as WebSetting<T>;
return typedOther.isPresent == isPresent && typedOther._value == _value;
}

Expand All @@ -382,19 +390,19 @@ class WebSettings {
this.hasNavigationDelegate,
this.debuggingEnabled,
this.gestureNavigationEnabled,
@required this.userAgent,
required this.userAgent,
}) : assert(userAgent != null);

/// The JavaScript execution mode to be used by the webview.
final JavascriptMode javascriptMode;
final JavascriptMode? javascriptMode;

/// Whether the [WebView] has a [NavigationDelegate] set.
final bool hasNavigationDelegate;
final bool? hasNavigationDelegate;

/// Whether to enable the platform's webview content debugging tools.
///
/// See also: [WebView.debuggingEnabled].
final bool debuggingEnabled;
final bool? debuggingEnabled;

/// The value used for the HTTP `User-Agent:` request header.
///
Expand All @@ -404,12 +412,12 @@ class WebSettings {
/// last time it was set.
///
/// See also [WebView.userAgent].
final WebSetting<String> userAgent;
final WebSetting<String?> userAgent;

/// Whether to allow swipe based navigation in iOS.
///
/// See also: [WebView.gestureNavigationEnabled]
final bool gestureNavigationEnabled;
final bool? gestureNavigationEnabled;

@override
String toString() {
Expand All @@ -428,7 +436,7 @@ class CreationParams {
CreationParams({
this.initialUrl,
this.webSettings,
this.javascriptChannelNames,
this.javascriptChannelNames = const <String>{},
this.userAgent,
this.autoMediaPlaybackPolicy =
AutoMediaPlaybackPolicy.require_user_action_for_all_media_types,
Expand All @@ -437,12 +445,12 @@ class CreationParams {
/// The initialUrl to load in the webview.
///
/// When null the webview will be created without loading any page.
final String initialUrl;
final String? initialUrl;

/// The initial [WebSettings] for the new webview.
///
/// This can later be updated with [WebViewPlatformController.updateSettings].
final WebSettings webSettings;
final WebSettings? webSettings;

/// The initial set of JavaScript channels that are configured for this webview.
///
Expand All @@ -460,7 +468,7 @@ class CreationParams {
/// The value used for the HTTP User-Agent: request header.
///
/// When null the platform's webview default is used for the User-Agent header.
final String userAgent;
final String? userAgent;

/// Which restrictions apply on automatic media playback.
final AutoMediaPlaybackPolicy autoMediaPlaybackPolicy;
Expand All @@ -475,7 +483,7 @@ class CreationParams {
///
/// See also the `onWebViewPlatformCreated` argument for [WebViewPlatform.build].
typedef WebViewPlatformCreatedCallback = void Function(
WebViewPlatformController webViewPlatformController);
WebViewPlatformController? webViewPlatformController);

/// Interface for a platform implementation of a WebView.
///
Expand Down Expand Up @@ -505,14 +513,14 @@ abstract class WebViewPlatform {
///
/// `webViewPlatformHandler` must not be null.
Widget build({
BuildContext context,
required 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,
required CreationParams creationParams,
required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
});

/// Clears all cookies for all [WebView] instances.
Expand Down
10 changes: 5 additions & 5 deletions packages/webview_flutter/lib/src/webview_android.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import 'webview_method_channel.dart';
class AndroidWebView implements WebViewPlatform {
@override
Widget build({
BuildContext context,
CreationParams creationParams,
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
required BuildContext context,
required CreationParams creationParams,
required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
}) {
assert(webViewPlatformCallbacksHandler != null);
return GestureDetector(
Expand Down
10 changes: 5 additions & 5 deletions packages/webview_flutter/lib/src/webview_cupertino.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ import 'webview_method_channel.dart';
class CupertinoWebView implements WebViewPlatform {
@override
Widget build({
BuildContext context,
CreationParams creationParams,
@required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>> gestureRecognizers,
required BuildContext context,
required CreationParams creationParams,
required WebViewPlatformCallbacksHandler webViewPlatformCallbacksHandler,
WebViewPlatformCreatedCallback? onWebViewPlatformCreated,
Set<Factory<OneSequenceGestureRecognizer>>? gestureRecognizers,
}) {
return UiKitView(
viewType: 'plugins.flutter.io/webview',
Expand Down
56 changes: 30 additions & 26 deletions packages/webview_flutter/lib/src/webview_method_channel.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,31 +25,31 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
static const MethodChannel _cookieManagerChannel =
MethodChannel('plugins.flutter.io/cookie_manager');

Future<bool> _onMethodCall(MethodCall call) async {
Future<bool?> _onMethodCall(MethodCall call) async {
switch (call.method) {
case 'javascriptChannelMessage':
final String channel = call.arguments['channel'];
final String message = call.arguments['message'];
final String channel = call.arguments['channel']!;
final String message = call.arguments['message']!;
_platformCallbacksHandler.onJavaScriptChannelMessage(channel, message);
return true;
case 'navigationRequest':
return await _platformCallbacksHandler.onNavigationRequest(
url: call.arguments['url'],
isForMainFrame: call.arguments['isForMainFrame'],
url: call.arguments['url']!,
isForMainFrame: call.arguments['isForMainFrame']!,
);
case 'onPageFinished':
_platformCallbacksHandler.onPageFinished(call.arguments['url']);
_platformCallbacksHandler.onPageFinished(call.arguments['url']!);
return null;
case 'onPageStarted':
_platformCallbacksHandler.onPageStarted(call.arguments['url']);
_platformCallbacksHandler.onPageStarted(call.arguments['url']!);
return null;
case 'onWebResourceError':
_platformCallbacksHandler.onWebResourceError(
WebResourceError(
errorCode: call.arguments['errorCode'],
description: call.arguments['description'],
errorCode: call.arguments['errorCode']!,
description: call.arguments['description']!,
failingUrl: call.arguments['failingUrl']!,
domain: call.arguments['domain'],
failingUrl: call.arguments['failingUrl'],
errorType: call.arguments['errorType'] == null
? null
: WebResourceErrorType.values.firstWhere(
Expand All @@ -71,7 +71,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
@override
Future<void> loadUrl(
String url,
Map<String, String> headers,
Map<String, String>? headers,
) async {
assert(url != null);
return _channel.invokeMethod<void>('loadUrl', <String, dynamic>{
Expand All @@ -81,13 +81,15 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
}

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

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

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

@override
Future<void> goBack() => _channel.invokeMethod<void>("goBack");
Expand All @@ -102,18 +104,18 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
Future<void> clearCache() => _channel.invokeMethod<void>("clearCache");

@override
Future<void> updateSettings(WebSettings settings) {
Future<void> updateSettings(WebSettings settings) async {
final Map<String, dynamic> updatesMap = _webSettingsToMap(settings);
if (updatesMap.isEmpty) {
return null;
if (updatesMap.isNotEmpty) {
await _channel.invokeMethod<void>('updateSettings', updatesMap);
}
return _channel.invokeMethod<void>('updateSettings', updatesMap);
}

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

@override
Expand All @@ -129,7 +131,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
}

@override
Future<String> getTitle() => _channel.invokeMethod<String>("getTitle");
Future<String?> getTitle() => _channel.invokeMethod<String>("getTitle");

@override
Future<void> scrollTo(int x, int y) {
Expand All @@ -148,19 +150,21 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
}

@override
Future<int> getScrollX() => _channel.invokeMethod<int>("getScrollX");
Future<int> getScrollX() =>
_channel.invokeMethod<int>("getScrollX").then((result) => result!);

@override
Future<int> getScrollY() => _channel.invokeMethod<int>("getScrollY");
Future<int> getScrollY() =>
_channel.invokeMethod<int>("getScrollY").then((result) => result!);

/// Method channel implementation for [WebViewPlatform.clearCookies].
static Future<bool> clearCookies() {
return _cookieManagerChannel
.invokeMethod<bool>('clearCookies')
.then<bool>((dynamic result) => result);
.then<bool>((dynamic result) => result!);
}

static Map<String, dynamic> _webSettingsToMap(WebSettings settings) {
static Map<String, dynamic> _webSettingsToMap(WebSettings? settings) {
final Map<String, dynamic> map = <String, dynamic>{};
void _addIfNonNull(String key, dynamic value) {
if (value == null) {
Expand All @@ -176,7 +180,7 @@ class MethodChannelWebViewPlatform implements WebViewPlatformController {
map[key] = setting.value;
}

_addIfNonNull('jsMode', settings.javascriptMode?.index);
_addIfNonNull('jsMode', settings!.javascriptMode?.index);
_addIfNonNull('hasNavigationDelegate', settings.hasNavigationDelegate);
_addIfNonNull('debuggingEnabled', settings.debuggingEnabled);
_addIfNonNull(
Expand Down
Loading