From 540362a06c2c14e14472ccd9dad8c31863c5baed Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Wed, 3 Jun 2020 09:00:18 -0700 Subject: [PATCH 1/8] Prepare url_launcher for the Link widget --- .../CHANGELOG.md | 4 + .../lib/link.dart | 99 +++++++++++++++++++ .../pubspec.yaml | 2 +- 3 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 packages/url_launcher/url_launcher_platform_interface/lib/link.dart diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md index 768042be4cef..10057e147adf 100644 --- a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.9 + +* Laid the groundwork for introducing a Link widget. + ## 1.0.8 * Added webOnlyWindowName parameter diff --git a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart new file mode 100644 index 000000000000..6df0e2c90a36 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -0,0 +1,99 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; +import 'dart:ui'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +/// Signature for a function provided by the [Link] widget that instructs it to +/// follow the link. +typedef FollowLink = Future Function(); + +/// Signature for a builder function passed to the [Link] widget to construct +/// the widget tree under it. +typedef LinkWidgetBuilder = Widget Function( + BuildContext context, + FollowLink followLink, +); + +/// Signature for a delegate function to build the [Link] widget. +typedef LinkDelegate = Widget Function(LinkInfo linkWidget); + +final MethodCodec _codec = const JSONMethodCodec(); + +/// Defines where a Link URL should be open. +/// +/// This is a class instead of an enum to allow future customizability e.g. +/// opening a link in a specific iframe. +class LinkTarget { + /// Const private constructor with a [debugLabel] to allow the creation of + /// multiple distinct const instances. + const LinkTarget._({this.debugLabel}); + + /// Used to distinguish multiple const instances of [LinkTarget]. + final String debugLabel; + + /// Use the default target for each platform. + /// + /// On Android, the default is [blank]. On the web, the default is [self]. + /// + /// iOS, on the other hand, defaults to [self] for web URLs, and [blank] for + /// non-web URLs. + static const defaultTarget = LinkTarget._(debugLabel: 'defaultTarget'); + + /// On the web, this opens the link in the same tab where the flutter app is + /// running. + /// + /// On Android and iOS, this opens the link in a webview within the app. + static const self = LinkTarget._(debugLabel: 'self'); + + /// On the web, this opens the link in a new tab or window (depending on the + /// browser and user configuration). + /// + /// On Android and iOS, this opens the link in the browser or the relevant + /// app. + static const blank = LinkTarget._(debugLabel: 'blank'); +} + +/// Used to override the delegate that builds the link. +LinkDelegate linkDelegate; + +/// Encapsulates all the information necessary to build a Link widget. +abstract class LinkInfo { + /// Called at build time to construct the widget tree under the link. + LinkWidgetBuilder get builder; + + /// The destination that this link leads to. + Uri get uri; + + /// The target indicating where to open the link. + LinkTarget get target; + + /// Whether the link is disabled or not. + bool get isDisabled; +} + +/// Pushes the [routeName] into Flutter's navigation system via a platform +/// message. +Future pushRouteNameToFramework( + String routeName, { + @required bool isUsingRouter, +}) { + final MethodCall call = isUsingRouter + ? MethodCall('pushRouteInformation', { + 'location': routeName, + 'state': null, + }) + : MethodCall('pushRoute', routeName); + final Completer completer = Completer(); + window.onPlatformMessage( + 'flutter/navigation', + _codec.encodeMethodCall(call), + completer.complete, + ); + return completer.future; +} diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index 0c4096278bcb..e10844a72bd1 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -3,7 +3,7 @@ description: A common platform interface for the url_launcher plugin. homepage: https://github.com/flutter/plugins/tree/master/packages/url_launcher/url_launcher_platform_interface # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 1.0.8 +version: 1.0.9 dependencies: flutter: From 5875f78d86c4efdff28d863a4bb869657399858a Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Thu, 15 Oct 2020 17:33:25 -0700 Subject: [PATCH 2/8] changes --- .../url_launcher_platform_interface/lib/link.dart | 11 +++++------ .../lib/method_channel_url_launcher.dart | 4 ++++ .../lib/url_launcher_platform_interface.dart | 4 ++++ 3 files changed, 13 insertions(+), 6 deletions(-) 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 6df0e2c90a36..102d19a4f2c8 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -59,9 +59,6 @@ class LinkTarget { static const blank = LinkTarget._(debugLabel: 'blank'); } -/// Used to override the delegate that builds the link. -LinkDelegate linkDelegate; - /// Encapsulates all the information necessary to build a Link widget. abstract class LinkInfo { /// Called at build time to construct the widget tree under the link. @@ -80,9 +77,11 @@ abstract class LinkInfo { /// Pushes the [routeName] into Flutter's navigation system via a platform /// message. Future pushRouteNameToFramework( - String routeName, { - @required bool isUsingRouter, -}) { + BuildContext context, + String routeName, +) { + final bool isUsingRouter = + context.findAncestorWidgetOfExactType() != null; final MethodCall call = isUsingRouter ? MethodCall('pushRouteInformation', { 'location': routeName, 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 f87630ee3045..ac5bfa230289 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 @@ -7,12 +7,16 @@ import 'dart:async'; import 'package:flutter/services.dart'; import 'package:meta/meta.dart' show required; +import 'link.dart'; import 'url_launcher_platform_interface.dart'; 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; + @override Future canLaunch(String url) { return _channel.invokeMethod( 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 1de5742c1f6f..75002ff9eb4d 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 @@ -6,6 +6,7 @@ import 'dart:async'; import 'package:meta/meta.dart' show required; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:url_launcher_platform_interface/link.dart'; import 'method_channel_url_launcher.dart'; @@ -38,6 +39,9 @@ abstract class UrlLauncherPlatform extends PlatformInterface { _instance = instance; } + /// The delegate used by the Link widget to build itself. + LinkDelegate get linkDelegate; + /// Returns `true` if this platform is able to launch [url]. Future canLaunch(String url) { throw UnimplementedError('canLaunch() has not been implemented.'); From 74ed33d4297f89fd64247926f724ab50d9f40384 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 16 Oct 2020 11:26:33 -0700 Subject: [PATCH 3/8] tests --- .../test/link_test.dart | 74 +++++++++++++++++++ .../method_channel_url_launcher_test.dart | 5 +- 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 packages/url_launcher/url_launcher_platform_interface/test/link_test.dart 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 new file mode 100644 index 000000000000..0d4b5ee44849 --- /dev/null +++ b/packages/url_launcher/url_launcher_platform_interface/test/link_test.dart @@ -0,0 +1,74 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:ui'; + +import 'package:mockito/mockito.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:meta/meta.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'package:url_launcher_platform_interface/link.dart'; +import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; + +final MethodCodec _codec = const JSONMethodCodec(); + +void main() { + TestWidgetsFlutterBinding.ensureInitialized(); + + PlatformMessageCallback oldHandler; + MethodCall lastCall; + + setUp(() { + oldHandler = window.onPlatformMessage; + window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { + lastCall = _codec.decodeMethodCall(data); + callback(_codec.encodeSuccessEnvelope(true)); + }; + }); + + tearDown(() { + window.onPlatformMessage = oldHandler; + }); + + test('pushRouteNameToFramework() calls pushRoute when no Router', () async { + final CustomBuildContext context = CustomBuildContext(router: null); + await pushRouteNameToFramework(context, '/foo/bar'); + expect(lastCall, isMethodCall( + 'pushRoute', + arguments: '/foo/bar', + )); + }); + + test('pushRouteNameToFramework() calls pushRouteInformation when Router exists', () async { + final CustomBuildContext context = CustomBuildContext(router: CustomRouter()); + await pushRouteNameToFramework(context, '/foo/bar'); + expect(lastCall, isMethodCall( + 'pushRouteInformation', + arguments: { + 'location': '/foo/bar', + 'state': null, + }, + )); + }); +} + +class CustomBuildContext extends Mock implements BuildContext { + CustomBuildContext({@required this.router}); + + final Router router; + + @override + S findAncestorWidgetOfExactType() { + expect(S, Router); + return router as S; + } +} + +class CustomRouter extends Mock implements Router { + @override + String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) => ''; +} diff --git a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart index 628ab48498ec..550a8faca7f1 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart @@ -286,4 +286,7 @@ class UrlLauncherPlatformMock extends Mock class ImplementsUrlLauncherPlatform extends Mock implements UrlLauncherPlatform {} -class ExtendsUrlLauncherPlatform extends UrlLauncherPlatform {} +class ExtendsUrlLauncherPlatform extends UrlLauncherPlatform { + @override + final LinkDelegate linkDelegate = null; +} From 5e1cc9863e444c497d68091dceb279aadb0d3bdd Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Fri, 16 Oct 2020 11:47:42 -0700 Subject: [PATCH 4/8] formatting and analysis --- .../lib/link.dart | 1 - .../test/link_test.dart | 51 ++++++++++++------- .../method_channel_url_launcher_test.dart | 1 + 3 files changed, 33 insertions(+), 20 deletions(-) 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 102d19a4f2c8..5808f79b9299 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -5,7 +5,6 @@ import 'dart:async'; import 'dart:ui'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; 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 0d4b5ee44849..0b8a239b01f0 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 @@ -9,10 +9,8 @@ import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:meta/meta.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:url_launcher_platform_interface/link.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; final MethodCodec _codec = const JSONMethodCodec(); @@ -24,7 +22,11 @@ void main() { setUp(() { oldHandler = window.onPlatformMessage; - window.onPlatformMessage = (String name, ByteData data, PlatformMessageResponseCallback callback) { + window.onPlatformMessage = ( + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) { lastCall = _codec.decodeMethodCall(data); callback(_codec.encodeSuccessEnvelope(true)); }; @@ -37,23 +39,33 @@ void main() { test('pushRouteNameToFramework() calls pushRoute when no Router', () async { final CustomBuildContext context = CustomBuildContext(router: null); await pushRouteNameToFramework(context, '/foo/bar'); - expect(lastCall, isMethodCall( - 'pushRoute', - arguments: '/foo/bar', - )); + expect( + lastCall, + isMethodCall( + 'pushRoute', + arguments: '/foo/bar', + ), + ); }); - test('pushRouteNameToFramework() calls pushRouteInformation when Router exists', () async { - final CustomBuildContext context = CustomBuildContext(router: CustomRouter()); - await pushRouteNameToFramework(context, '/foo/bar'); - expect(lastCall, isMethodCall( - 'pushRouteInformation', - arguments: { - 'location': '/foo/bar', - 'state': null, - }, - )); - }); + test( + 'pushRouteNameToFramework() calls pushRouteInformation when Router exists', + () async { + final CustomBuildContext context = + CustomBuildContext(router: CustomRouter()); + await pushRouteNameToFramework(context, '/foo/bar'); + expect( + lastCall, + isMethodCall( + 'pushRouteInformation', + arguments: { + 'location': '/foo/bar', + 'state': null, + }, + ), + ); + }, + ); } class CustomBuildContext extends Mock implements BuildContext { @@ -68,7 +80,8 @@ class CustomBuildContext extends Mock implements BuildContext { } } +// ignore: must_be_immutable class CustomRouter extends Mock implements Router { @override - String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) => ''; + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) => ''; } diff --git a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart index 550a8faca7f1..d88f53ad58d0 100644 --- a/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart +++ b/packages/url_launcher/url_launcher_platform_interface/test/method_channel_url_launcher_test.dart @@ -7,6 +7,7 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/method_channel_url_launcher.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; From ad4c1b1c643d14a3264df554c5f428de91ac6efb Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Sun, 18 Oct 2020 01:57:21 -0700 Subject: [PATCH 5/8] handle Router correctly --- .../lib/link.dart | 36 +++++++++++-------- .../test/link_test.dart | 30 ++++------------ 2 files changed, 29 insertions(+), 37 deletions(-) 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 5808f79b9299..dacfee0b9efc 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -77,21 +77,29 @@ abstract class LinkInfo { /// message. Future pushRouteNameToFramework( BuildContext context, - String routeName, -) { - final bool isUsingRouter = - context.findAncestorWidgetOfExactType() != null; - final MethodCall call = isUsingRouter - ? MethodCall('pushRouteInformation', { + String routeName, { + @visibleForTesting + bool debugForceRouter = false, +}) { + final Completer completer = Completer(); + if (debugForceRouter || Router.of(context, nullOk: true) != null) { + SystemNavigator.routeInformationUpdated(location: routeName); + window.onPlatformMessage( + 'flutter/navigation', + _codec.encodeMethodCall( + MethodCall('pushRouteInformation', { 'location': routeName, 'state': null, - }) - : MethodCall('pushRoute', routeName); - final Completer completer = Completer(); - window.onPlatformMessage( - 'flutter/navigation', - _codec.encodeMethodCall(call), - completer.complete, - ); + }), + ), + completer.complete, + ); + } else { + window.onPlatformMessage( + 'flutter/navigation', + _codec.encodeMethodCall(MethodCall('pushRoute', routeName)), + completer.complete, + ); + } return completer.future; } 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 0b8a239b01f0..99a885ccc179 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 @@ -8,7 +8,6 @@ import 'package:mockito/mockito.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:meta/meta.dart'; import 'package:url_launcher_platform_interface/link.dart'; @@ -37,8 +36,7 @@ void main() { }); test('pushRouteNameToFramework() calls pushRoute when no Router', () async { - final CustomBuildContext context = CustomBuildContext(router: null); - await pushRouteNameToFramework(context, '/foo/bar'); + await pushRouteNameToFramework(CustomBuildContext(), '/foo/bar'); expect( lastCall, isMethodCall( @@ -51,9 +49,11 @@ void main() { test( 'pushRouteNameToFramework() calls pushRouteInformation when Router exists', () async { - final CustomBuildContext context = - CustomBuildContext(router: CustomRouter()); - await pushRouteNameToFramework(context, '/foo/bar'); + await pushRouteNameToFramework( + CustomBuildContext(), + '/foo/bar', + debugForceRouter: true, + ); expect( lastCall, isMethodCall( @@ -68,20 +68,4 @@ void main() { ); } -class CustomBuildContext extends Mock implements BuildContext { - CustomBuildContext({@required this.router}); - - final Router router; - - @override - S findAncestorWidgetOfExactType() { - expect(S, Router); - return router as S; - } -} - -// ignore: must_be_immutable -class CustomRouter extends Mock implements Router { - @override - String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) => ''; -} +class CustomBuildContext extends Mock implements BuildContext {} From b26f8e65afeb38ea21a2b44963ffc1ebbb0762e2 Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Sun, 18 Oct 2020 02:29:31 -0700 Subject: [PATCH 6/8] bump min version of flutter sdk --- .../url_launcher/url_launcher_platform_interface/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index e10844a72bd1..d628699f97dc 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -19,4 +19,4 @@ dev_dependencies: environment: sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.9.1+hotfix.4 <2.0.0" + flutter: ">=1.23.0-18.0.pre <2.0.0" From 7cea39f4483ef1372ca8dad552521c667f2bb7ff Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Sun, 18 Oct 2020 02:30:14 -0700 Subject: [PATCH 7/8] formatting --- .../url_launcher/url_launcher_platform_interface/lib/link.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 dacfee0b9efc..17a527802cd1 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -78,8 +78,7 @@ abstract class LinkInfo { Future pushRouteNameToFramework( BuildContext context, String routeName, { - @visibleForTesting - bool debugForceRouter = false, + @visibleForTesting bool debugForceRouter = false, }) { final Completer completer = Completer(); if (debugForceRouter || Router.of(context, nullOk: true) != null) { From 247991c90ddd72cdeace0c8bbf0582fba3389acb Mon Sep 17 00:00:00 2001 From: Mouad Debbar Date: Mon, 19 Oct 2020 10:34:13 -0700 Subject: [PATCH 8/8] version --- .../url_launcher_platform_interface/lib/link.dart | 11 ++++++++++- .../url_launcher_platform_interface/pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) 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 17a527802cd1..425dc886d29f 100644 --- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart +++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart @@ -81,7 +81,7 @@ Future pushRouteNameToFramework( @visibleForTesting bool debugForceRouter = false, }) { final Completer completer = Completer(); - if (debugForceRouter || Router.of(context, nullOk: true) != null) { + if (debugForceRouter || _hasRouter(context)) { SystemNavigator.routeInformationUpdated(location: routeName); window.onPlatformMessage( 'flutter/navigation', @@ -102,3 +102,12 @@ Future pushRouteNameToFramework( } return completer.future; } + +bool _hasRouter(BuildContext context) { + try { + return Router.of(context) != null; + } on AssertionError { + // When a `Router` can't be found, an assertion error is thrown. + return false; + } +} diff --git a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml index d628699f97dc..ce0fdd936c9a 100644 --- a/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml +++ b/packages/url_launcher/url_launcher_platform_interface/pubspec.yaml @@ -19,4 +19,4 @@ dev_dependencies: environment: sdk: ">=2.1.0 <3.0.0" - flutter: ">=1.23.0-18.0.pre <2.0.0" + flutter: ">=1.22.0 <2.0.0"