[url_launcher] Migrate pushRouteNameToFramework to ChannelBuffers (#3893)
diff --git a/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md b/packages/url_launcher/url_launcher_platform_interface/CHANGELOG.md
index 72fa831..06a2efe 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 @@
+## 2.0.3
+
+* Migrate `pushRouteNameToFramework` to use ChannelBuffers API.
+
## 2.0.2
* Update platform_plugin_interface version requirement.
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 9784a3d..4a414ae 100644
--- a/packages/url_launcher/url_launcher_platform_interface/lib/link.dart
+++ b/packages/url_launcher/url_launcher_platform_interface/lib/link.dart
@@ -3,10 +3,10 @@
// found in the LICENSE file.
import 'dart:async';
-import 'dart:ui';
+import 'dart:ui' as ui;
-import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:flutter/widgets.dart';
/// Signature for a function provided by the [Link] widget that instructs it to
/// follow the link.
@@ -73,45 +73,32 @@
bool get isDisabled;
}
+typedef _SendMessage = Function(String, ByteData?, void Function(ByteData?));
+
/// Pushes the [routeName] into Flutter's navigation system via a platform
/// message.
-Future<ByteData> pushRouteNameToFramework(
- BuildContext context,
- String routeName, {
- @visibleForTesting bool debugForceRouter = false,
-}) {
- final PlatformMessageCallback? onPlatformMessage = window.onPlatformMessage;
- if (onPlatformMessage == null) {
- return Future<ByteData>.value(null);
- }
+///
+/// The platform is notified using [SystemNavigator.routeInformationUpdated]. On
+/// older versions of Flutter, this means it will not work unless the
+/// application uses a [Router] (e.g. using [MaterialApp.router]).
+///
+/// Returns the raw data returned by the framework.
+// TODO(ianh): Remove the first argument.
+Future<ByteData> pushRouteNameToFramework(Object? _, String routeName) {
final Completer<ByteData> completer = Completer<ByteData>();
- if (debugForceRouter || _hasRouter(context)) {
- SystemNavigator.routeInformationUpdated(location: routeName);
- onPlatformMessage(
- 'flutter/navigation',
- _codec.encodeMethodCall(
- MethodCall('pushRouteInformation', <dynamic, dynamic>{
- 'location': routeName,
- 'state': null,
- }),
- ),
- completer.complete,
- );
- } else {
- onPlatformMessage(
- 'flutter/navigation',
- _codec.encodeMethodCall(MethodCall('pushRoute', routeName)),
- completer.complete,
- );
- }
+ SystemNavigator.routeInformationUpdated(location: routeName);
+ final _SendMessage sendMessage =
+ WidgetsBinding.instance?.platformDispatcher.onPlatformMessage ??
+ ui.channelBuffers.push;
+ sendMessage(
+ 'flutter/navigation',
+ _codec.encodeMethodCall(
+ MethodCall('pushRouteInformation', <dynamic, dynamic>{
+ 'location': routeName,
+ 'state': null,
+ }),
+ ),
+ completer.complete,
+ );
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 4411566..80002d1 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 @@
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: 2.0.2
+version: 2.0.3
dependencies:
flutter:
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 638a655..1f45b59 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,72 +2,87 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-import 'dart:ui';
+import 'dart:collection';
-import 'package:mockito/mockito.dart';
-import 'package:flutter/services.dart';
-import 'package:flutter/widgets.dart';
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:url_launcher_platform_interface/link.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);
- if (callback != null) {
- callback(_codec.encodeSuccessEnvelope(true));
- }
- };
+ testWidgets('Link with Navigator', (WidgetTester tester) async {
+ await tester.pumpWidget(MaterialApp(
+ home: Placeholder(key: Key('home')),
+ routes: <String, WidgetBuilder>{
+ '/a': (BuildContext context) => Placeholder(key: Key('a')),
+ },
+ ));
+ expect(find.byKey(Key('home')), findsOneWidget);
+ expect(find.byKey(Key('a')), findsNothing);
+ await pushRouteNameToFramework(null, '/a');
+ // start animation
+ await tester.pump();
+ // skip past animation (5s is arbitrary, just needs to be long enough)
+ await tester.pump(const Duration(seconds: 5));
+ expect(find.byKey(Key('a')), findsOneWidget);
+ expect(find.byKey(Key('home')), findsNothing);
});
- tearDown(() {
- window.onPlatformMessage = oldHandler;
+ testWidgets('Link with Navigator', (WidgetTester tester) async {
+ await tester.pumpWidget(MaterialApp.router(
+ routeInformationParser: _RouteInformationParser(),
+ routerDelegate: _RouteDelegate(),
+ ));
+ expect(find.byKey(Key('/')), findsOneWidget);
+ expect(find.byKey(Key('/a')), findsNothing);
+ await pushRouteNameToFramework(null, '/a');
+ // start animation
+ await tester.pump();
+ // skip past animation (5s is arbitrary, just needs to be long enough)
+ await tester.pump(const Duration(seconds: 5));
+ expect(find.byKey(Key('/a')), findsOneWidget);
+ expect(find.byKey(Key('/')), findsNothing);
});
-
- test('pushRouteNameToFramework() calls pushRoute when no Router', () async {
- await pushRouteNameToFramework(CustomBuildContext(), '/foo/bar');
- expect(
- lastCall,
- isMethodCall(
- 'pushRoute',
- arguments: '/foo/bar',
- ),
- );
- });
-
- test(
- 'pushRouteNameToFramework() calls pushRouteInformation when Router exists',
- () async {
- await pushRouteNameToFramework(
- CustomBuildContext(),
- '/foo/bar',
- debugForceRouter: true,
- );
- expect(
- lastCall,
- isMethodCall(
- 'pushRouteInformation',
- arguments: <dynamic, dynamic>{
- 'location': '/foo/bar',
- 'state': null,
- },
- ),
- );
- },
- );
}
-class CustomBuildContext<T> extends Mock implements BuildContext {}
+class _RouteInformationParser extends RouteInformationParser<RouteInformation> {
+ @override
+ Future<RouteInformation> parseRouteInformation(
+ RouteInformation routeInformation) {
+ return SynchronousFuture(routeInformation);
+ }
+
+ @override
+ RouteInformation? restoreRouteInformation(RouteInformation configuration) {
+ return configuration;
+ }
+}
+
+class _RouteDelegate extends RouterDelegate<RouteInformation>
+ with ChangeNotifier {
+ final Queue<RouteInformation> _history = Queue<RouteInformation>();
+
+ @override
+ Future<void> setNewRoutePath(RouteInformation configuration) {
+ _history.add(configuration);
+ return SynchronousFuture(null);
+ }
+
+ @override
+ Future<bool> popRoute() {
+ if (_history.isEmpty) {
+ return SynchronousFuture(false);
+ }
+ _history.removeLast();
+ return SynchronousFuture(true);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (_history.isEmpty) {
+ return Placeholder(key: Key('empty'));
+ }
+ return Placeholder(key: Key('${_history.last.location}'));
+ }
+}