From d7cc98d4b6e4851796cb0346ae22a5032a3653d6 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 23 Oct 2020 13:35:30 -0700 Subject: [PATCH 1/3] Migrate Link to null safety --- .../url_launcher/lib/src/link.dart | 20 +++++++++---------- .../url_launcher/url_launcher/pubspec.yaml | 10 ++++++---- .../url_launcher/test/link_test.dart | 3 ++- .../lib/link.dart | 13 +++++++----- .../lib/method_channel_url_launcher.dart | 2 +- .../lib/url_launcher_platform_interface.dart | 2 +- .../test/link_test.dart | 3 +++ 7 files changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index bd54789accfb..7c6f4410ef4e 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -40,7 +40,7 @@ class Link extends StatelessWidget implements LinkInfo { final LinkWidgetBuilder builder; /// The destination that this link leads to. - final Uri uri; + final Uri? uri; /// The target indicating where to open the link. final LinkTarget target; @@ -51,11 +51,11 @@ class Link extends StatelessWidget implements LinkInfo { /// Creates a widget that renders a real link on the web, and uses WebViews in /// native platforms to open links. Link({ - Key key, - @required this.uri, - LinkTarget target, - @required this.builder, - }) : target = target ?? LinkTarget.defaultTarget, + Key? key, + required this.uri, + LinkTarget? target, + required this.builder, + }) : target = target ?? LinkTarget.defaultTarget, super(key: key); LinkDelegate get _effectiveDelegate { @@ -90,16 +90,17 @@ class DefaultLinkDelegate extends StatelessWidget { bool get _useWebView { if (link.target == LinkTarget.self) return true; if (link.target == LinkTarget.blank) return false; - return null; + return false; } Future _followLink(BuildContext context) async { - if (!link.uri.hasScheme) { + if (link.uri == null || !link.uri!.hasScheme) { // A uri that doesn't have a scheme is an internal route name. In this // case, we push it via Flutter's navigation system instead of letting the // browser handle it. final String routeName = link.uri.toString(); - return pushRouteNameToFramework(context, routeName); + await pushRouteNameToFramework(context, routeName); + return; } // At this point, we know that the link is external. So we use the `launch` @@ -119,7 +120,6 @@ class DefaultLinkDelegate extends StatelessWidget { context: ErrorDescription('during launching a link'), )); } - return Future.value(null); } @override diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 9e0cae460726..36bdb338520c 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -27,10 +27,12 @@ dependencies: sdk: flutter # TODO(mvanbeusekom): Update to use pub.dev once null safety version is published. url_launcher_platform_interface: - git: - url: https://github.com/flutter/plugins.git - ref: nnbd - path: packages/url_launcher/url_launcher_platform_interface + path: ../url_launcher_platform_interface + + # git: + # url: https://github.com/flutter/plugins.git + # ref: nnbd + # path: packages/url_launcher/url_launcher_platform_interface # The design on https://flutter.dev/go/federated-plugins was to leave # this constraint as "any". We cannot do it right now as it fails pub publish # validation, so we set a ^ constraint. diff --git a/packages/url_launcher/url_launcher/test/link_test.dart b/packages/url_launcher/url_launcher/test/link_test.dart index d525153dc0a0..46903aadaede 100644 --- a/packages/url_launcher/url_launcher/test/link_test.dart +++ b/packages/url_launcher/url_launcher/test/link_test.dart @@ -2,7 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -// @dart = 2.8 +// TODO(egarciad): Remove once Mockito has been migrated to null safety. +// @dart = 2.9 import 'dart:ui'; import 'package:flutter/material.dart'; diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart index 425dc886d29f..8e70634d8833 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -16,7 +16,7 @@ typedef FollowLink = Future Function(); /// the widget tree under it. typedef LinkWidgetBuilder = Widget Function( BuildContext context, - FollowLink followLink, + FollowLink? followLink, ); /// Signature for a delegate function to build the [Link] widget. @@ -31,7 +31,7 @@ final MethodCodec _codec = const JSONMethodCodec(); class LinkTarget { /// Const private constructor with a [debugLabel] to allow the creation of /// multiple distinct const instances. - const LinkTarget._({this.debugLabel}); + const LinkTarget._({required this.debugLabel}); /// Used to distinguish multiple const instances of [LinkTarget]. final String debugLabel; @@ -64,7 +64,7 @@ abstract class LinkInfo { LinkWidgetBuilder get builder; /// The destination that this link leads to. - Uri get uri; + Uri? get uri; /// The target indicating where to open the link. LinkTarget get target; @@ -80,10 +80,13 @@ Future pushRouteNameToFramework( String routeName, { @visibleForTesting bool debugForceRouter = false, }) { + if (window.onPlatformMessage == null) { + return Future.value(null); + } final Completer completer = Completer(); if (debugForceRouter || _hasRouter(context)) { SystemNavigator.routeInformationUpdated(location: routeName); - window.onPlatformMessage( + window.onPlatformMessage!( 'flutter/navigation', _codec.encodeMethodCall( MethodCall('pushRouteInformation', { @@ -94,7 +97,7 @@ Future pushRouteNameToFramework( completer.complete, ); } else { - window.onPlatformMessage( + window.onPlatformMessage!( 'flutter/navigation', _codec.encodeMethodCall(MethodCall('pushRoute', routeName)), completer.complete, diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart index a67d045c563e..7b9dfc9cc5cf 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/method_channel_url_launcher.dart @@ -14,7 +14,7 @@ const MethodChannel _channel = MethodChannel('plugins.flutter.io/url_launcher'); /// An implementation of [UrlLauncherPlatform] that uses method channels. class MethodChannelUrlLauncher extends UrlLauncherPlatform { @override - final LinkDelegate linkDelegate = null; + final LinkDelegate? linkDelegate = null; @override Future canLaunch(String url) { diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart index 2969002bc71a..2a4edfa8d1af 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/url_launcher_platform_interface.dart @@ -39,7 +39,7 @@ abstract class UrlLauncherPlatform extends PlatformInterface { } /// The delegate used by the Link widget to build itself. - LinkDelegate get linkDelegate; + LinkDelegate? get linkDelegate; /// Returns `true` if this platform is able to launch [url]. Future canLaunch(String url) { diff --git a/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart index 99a885ccc179..58cdd22dca02 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +// TODO(egarciad): Remove once Mockito has been migrated to null safety. +// @dart = 2.9 + import 'dart:ui'; import 'package:mockito/mockito.dart'; From bfd2ee2aae23bc156c6cb3e16efdd3d620890d6e Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 23 Oct 2020 13:37:02 -0700 Subject: [PATCH 2/3] Update pubspec.yaml --- packages/url_launcher/url_launcher/pubspec.yaml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/url_launcher/url_launcher/pubspec.yaml b/packages/url_launcher/url_launcher/pubspec.yaml index 36bdb338520c..9e0cae460726 100644 --- a/packages/url_launcher/url_launcher/pubspec.yaml +++ b/packages/url_launcher/url_launcher/pubspec.yaml @@ -27,12 +27,10 @@ dependencies: sdk: flutter # TODO(mvanbeusekom): Update to use pub.dev once null safety version is published. url_launcher_platform_interface: - path: ../url_launcher_platform_interface - - # git: - # url: https://github.com/flutter/plugins.git - # ref: nnbd - # path: packages/url_launcher/url_launcher_platform_interface + git: + url: https://github.com/flutter/plugins.git + ref: nnbd + path: packages/url_launcher/url_launcher_platform_interface # The design on https://flutter.dev/go/federated-plugins was to leave # this constraint as "any". We cannot do it right now as it fails pub publish # validation, so we set a ^ constraint. From e9782275cdbed5d4bcb5af4a45c62aa5e6f38a6c Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 23 Oct 2020 14:47:54 -0700 Subject: [PATCH 3/3] Feedback --- packages/url_launcher/url_launcher/lib/src/link.dart | 7 +++---- .../url_launcher_platform_interface/lib/link.dart | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/url_launcher/url_launcher/lib/src/link.dart b/packages/url_launcher/url_launcher/lib/src/link.dart index 7c6f4410ef4e..f859bc4ad2cf 100644 --- a/packages/url_launcher/url_launcher/lib/src/link.dart +++ b/packages/url_launcher/url_launcher/lib/src/link.dart @@ -53,10 +53,9 @@ class Link extends StatelessWidget implements LinkInfo { Link({ Key? key, required this.uri, - LinkTarget? target, + this.target = LinkTarget.defaultTarget, required this.builder, - }) : target = target ?? LinkTarget.defaultTarget, - super(key: key); + }) : super(key: key); LinkDelegate get _effectiveDelegate { return UrlLauncherPlatform.instance.linkDelegate ?? @@ -94,7 +93,7 @@ class DefaultLinkDelegate extends StatelessWidget { } Future _followLink(BuildContext context) async { - if (link.uri == null || !link.uri!.hasScheme) { + if (!link.uri!.hasScheme) { // A uri that doesn't have a scheme is an internal route name. In this // case, we push it via Flutter's navigation system instead of letting the // browser handle it. diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart index 8e70634d8833..a176972e06bd 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -80,13 +80,14 @@ Future pushRouteNameToFramework( String routeName, { @visibleForTesting bool debugForceRouter = false, }) { - if (window.onPlatformMessage == null) { + final PlatformMessageCallback? onPlatformMessage = window.onPlatformMessage; + if (onPlatformMessage == null) { return Future.value(null); } final Completer completer = Completer(); if (debugForceRouter || _hasRouter(context)) { SystemNavigator.routeInformationUpdated(location: routeName); - window.onPlatformMessage!( + onPlatformMessage( 'flutter/navigation', _codec.encodeMethodCall( MethodCall('pushRouteInformation', { @@ -97,7 +98,7 @@ Future pushRouteNameToFramework( completer.complete, ); } else { - window.onPlatformMessage!( + onPlatformMessage( 'flutter/navigation', _codec.encodeMethodCall(MethodCall('pushRoute', routeName)), completer.complete,