From 92ef2b9ce1d4e54d5efb3e6a962e9e724dc7efeb Mon Sep 17 00:00:00 2001 From: adazh <46544665+adazh@users.noreply.github.com> Date: Fri, 9 Aug 2019 10:13:40 -0700 Subject: [PATCH] Moved the default BinaryMessenger instance to ServicesBinding (#37489) --- .../flutter_test/ticker_test.dart | 2 +- .../test_driver/main.dart | 4 +- .../ios_add2app/flutterapp/lib/main.dart | 2 + .../lib/src/services/asset_bundle.dart | 5 +- .../lib/src/services/binary_messenger.dart | 99 ++-------------- .../flutter/lib/src/services/binding.dart | 107 ++++++++++++++++++ .../lib/src/services/platform_channel.dart | 49 ++++---- .../lib/src/services/platform_messages.dart | 11 +- packages/flutter/lib/src/widgets/binding.dart | 9 +- .../test/cupertino/text_field_test.dart | 1 + .../foundation/service_extensions_test.dart | 4 +- .../flutter/test/material/search_test.dart | 2 +- .../test/material/text_field_test.dart | 3 +- .../flutter/test/scheduler/ticker_test.dart | 6 +- .../semantics/semantics_service_test.dart | 5 + .../test/services/fake_platform_views.dart | 2 +- .../test/services/haptic_feedback_test.dart | 4 + .../test/services/platform_channel_test.dart | 40 ++++--- .../test/services/platform_messages_test.dart | 8 +- .../test/services/platform_views_test.dart | 5 + .../test/services/system_chrome_test.dart | 2 +- .../test/services/system_navigator_test.dart | 4 + .../test/services/system_sound_test.dart | 4 + .../flutter/test/widgets/binding_test.dart | 6 +- .../test/widgets/focus_manager_test.dart | 2 +- .../widgets/raw_keyboard_listener_test.dart | 2 +- .../test/widgets/selectable_text_test.dart | 3 +- .../flutter/test/widgets/shortcuts_test.dart | 2 +- packages/flutter_test/lib/src/binding.dart | 4 +- .../flutter_test/lib/src/test_text_input.dart | 7 +- 30 files changed, 243 insertions(+), 161 deletions(-) diff --git a/dev/automated_tests/flutter_test/ticker_test.dart b/dev/automated_tests/flutter_test/ticker_test.dart index 50f6d963cf..7bd9e185ac 100644 --- a/dev/automated_tests/flutter_test/ticker_test.dart +++ b/dev/automated_tests/flutter_test/ticker_test.dart @@ -11,6 +11,6 @@ void main() { Ticker((Duration duration) { })..start(); final ByteData message = const StringCodec().encodeMessage('AppLifecycleState.paused'); - await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) {}); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) {}); }); } diff --git a/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/test_driver/main.dart b/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/test_driver/main.dart index b939220f81..b04fbf6d81 100644 --- a/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/test_driver/main.dart +++ b/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/test_driver/main.dart @@ -36,9 +36,9 @@ Future respondToHostRequestForSplashLog(String _) { void createTestChannelBetweenAndroidAndFlutter() { // Channel used for Android to send Flutter changes to the splash display. - final BasicMessageChannel testChannel = BasicMessageChannel( + const BasicMessageChannel testChannel = BasicMessageChannel( 'testChannel', - const StringCodec() + StringCodec() ); // Every splash display change message that we receive from Android is either diff --git a/dev/integration_tests/ios_add2app/flutterapp/lib/main.dart b/dev/integration_tests/ios_add2app/flutterapp/lib/main.dart index ea7fe54cb4..fff451158e 100644 --- a/dev/integration_tests/ios_add2app/flutterapp/lib/main.dart +++ b/dev/integration_tests/ios_add2app/flutterapp/lib/main.dart @@ -25,6 +25,8 @@ const BasicMessageChannel _kReloadChannel = BasicMessageChannel(_kReloadChannelName, StringCodec()); void main() { + // Ensures bindings are initialized before doing anything. + WidgetsFlutterBinding.ensureInitialized(); // Start listening immediately for messages from the iOS side. ObjC calls // will be made to let us know when we should be changing the app state. _kReloadChannel.setMessageHandler(run); diff --git a/packages/flutter/lib/src/services/asset_bundle.dart b/packages/flutter/lib/src/services/asset_bundle.dart index e6921c0e77..23c4e9cdb4 100644 --- a/packages/flutter/lib/src/services/asset_bundle.dart +++ b/packages/flutter/lib/src/services/asset_bundle.dart @@ -9,7 +9,7 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; -import 'binary_messenger.dart'; +import 'binding.dart'; /// A collection of resources used by the application. /// @@ -212,11 +212,12 @@ abstract class CachingAssetBundle extends AssetBundle { /// An [AssetBundle] that loads resources using platform messages. class PlatformAssetBundle extends CachingAssetBundle { + @override Future load(String key) async { final Uint8List encoded = utf8.encoder.convert(Uri(path: Uri.encodeFull(key)).path); final ByteData asset = - await defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData()); + await ServicesBinding.instance.defaultBinaryMessenger.send('flutter/assets', encoded.buffer.asByteData()); if (asset == null) throw FlutterError('Unable to load asset: $key'); return asset; diff --git a/packages/flutter/lib/src/services/binary_messenger.dart b/packages/flutter/lib/src/services/binary_messenger.dart index b246f0530d..9ac647a345 100644 --- a/packages/flutter/lib/src/services/binary_messenger.dart +++ b/packages/flutter/lib/src/services/binary_messenger.dart @@ -6,7 +6,7 @@ import 'dart:async'; import 'dart:typed_data'; import 'dart:ui' as ui; -import 'package:flutter/foundation.dart'; +import 'binding.dart'; /// A function which takes a platform message and asynchronously returns an encoded response. typedef MessageHandler = Future Function(ByteData message); @@ -56,99 +56,14 @@ abstract class BinaryMessenger { void setMockMessageHandler(String channel, Future handler(ByteData message)); } -/// The default implementation of [BinaryMessenger]. -/// -/// This messenger sends messages from the app-side to the platform-side and -/// dispatches incoming messages from the platform-side to the appropriate -/// handler. -class _DefaultBinaryMessenger extends BinaryMessenger { - const _DefaultBinaryMessenger._(); - - // Handlers for incoming messages from platform plugins. - // This is static so that this class can have a const constructor. - static final Map _handlers = - {}; - - // Mock handlers that intercept and respond to outgoing messages. - // This is static so that this class can have a const constructor. - static final Map _mockHandlers = - {}; - - Future _sendPlatformMessage(String channel, ByteData message) { - final Completer completer = Completer(); - // ui.window is accessed directly instead of using ServicesBinding.instance.window - // because this method might be invoked before any binding is initialized. - // This issue was reported in #27541. It is not ideal to statically access - // ui.window because the Window may be dependency injected elsewhere with - // a different instance. However, static access at this location seems to be - // the least bad option. - ui.window.sendPlatformMessage(channel, message, (ByteData reply) { - try { - completer.complete(reply); - } catch (exception, stack) { - FlutterError.reportError(FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'services library', - context: ErrorDescription('during a platform message response callback'), - )); - } - }); - return completer.future; - } - - @override - Future handlePlatformMessage( - String channel, - ByteData data, - ui.PlatformMessageResponseCallback callback, - ) async { - ByteData response; - try { - final MessageHandler handler = _handlers[channel]; - if (handler != null) - response = await handler(data); - } catch (exception, stack) { - FlutterError.reportError(FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'services library', - context: ErrorDescription('during a platform message callback'), - )); - } finally { - callback(response); - } - } - - - @override - Future send(String channel, ByteData message) { - final MessageHandler handler = _mockHandlers[channel]; - if (handler != null) - return handler(message); - return _sendPlatformMessage(channel, message); - } - - @override - void setMessageHandler(String channel, MessageHandler handler) { - if (handler == null) - _handlers.remove(channel); - else - _handlers[channel] = handler; - } - - @override - void setMockMessageHandler(String channel, MessageHandler handler) { - if (handler == null) - _mockHandlers.remove(channel); - else - _mockHandlers[channel] = handler; - } -} - /// The default instance of [BinaryMessenger]. /// +/// This API has been deprecated in favor of [ServicesBinding.defaultBinaryMessenger]. +/// Please use [ServicesBinding.defaultBinaryMessenger] as the default +/// instance of [BinaryMessenger]. +/// /// This is used to send messages from the application to the platform, and /// keeps track of which handlers have been registered on each channel so /// it may dispatch incoming messages to the registered handler. -const BinaryMessenger defaultBinaryMessenger = _DefaultBinaryMessenger._(); +@Deprecated('Use ServicesBinding.instance.defaultBinaryMessenger instead.') +BinaryMessenger get defaultBinaryMessenger => ServicesBinding.instance.defaultBinaryMessenger; diff --git a/packages/flutter/lib/src/services/binding.dart b/packages/flutter/lib/src/services/binding.dart index 7cc1f3525c..2fb3d3b433 100644 --- a/packages/flutter/lib/src/services/binding.dart +++ b/packages/flutter/lib/src/services/binding.dart @@ -3,6 +3,8 @@ // found in the LICENSE file. import 'dart:async'; +import 'dart:typed_data'; +import 'dart:ui' as ui; import 'package:flutter/foundation.dart'; @@ -20,6 +22,7 @@ mixin ServicesBinding on BindingBase { void initInstances() { super.initInstances(); _instance = this; + _defaultBinaryMessenger = createBinaryMessenger(); window ..onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage; initLicenses(); @@ -29,6 +32,21 @@ mixin ServicesBinding on BindingBase { static ServicesBinding get instance => _instance; static ServicesBinding _instance; + /// The default instance of [BinaryMessenger]. + /// + /// This is used to send messages from the application to the platform, and + /// keeps track of which handlers have been registered on each channel so + /// it may dispatch incoming messages to the registered handler. + BinaryMessenger get defaultBinaryMessenger => _defaultBinaryMessenger; + BinaryMessenger _defaultBinaryMessenger; + + /// Creates a default [BinaryMessenger] instance that can be used for sending + /// platform messages. + @protected + BinaryMessenger createBinaryMessenger() { + return const _DefaultBinaryMessenger._(); + } + /// Adds relevant licenses to the [LicenseRegistry]. /// /// By default, the [ServicesBinding]'s implementation of [initLicenses] adds @@ -117,3 +135,92 @@ mixin ServicesBinding on BindingBase { rootBundle.evict(asset); } } + +/// The default implementation of [BinaryMessenger]. +/// +/// This messenger sends messages from the app-side to the platform-side and +/// dispatches incoming messages from the platform-side to the appropriate +/// handler. +class _DefaultBinaryMessenger extends BinaryMessenger { + const _DefaultBinaryMessenger._(); + + // Handlers for incoming messages from platform plugins. + // This is static so that this class can have a const constructor. + static final Map _handlers = + {}; + + // Mock handlers that intercept and respond to outgoing messages. + // This is static so that this class can have a const constructor. + static final Map _mockHandlers = + {}; + + Future _sendPlatformMessage(String channel, ByteData message) { + final Completer completer = Completer(); + // ui.window is accessed directly instead of using ServicesBinding.instance.window + // because this method might be invoked before any binding is initialized. + // This issue was reported in #27541. It is not ideal to statically access + // ui.window because the Window may be dependency injected elsewhere with + // a different instance. However, static access at this location seems to be + // the least bad option. + ui.window.sendPlatformMessage(channel, message, (ByteData reply) { + try { + completer.complete(reply); + } catch (exception, stack) { + FlutterError.reportError(FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'services library', + context: ErrorDescription('during a platform message response callback'), + )); + } + }); + return completer.future; + } + + @override + Future handlePlatformMessage( + String channel, + ByteData data, + ui.PlatformMessageResponseCallback callback, + ) async { + ByteData response; + try { + final MessageHandler handler = _handlers[channel]; + if (handler != null) + response = await handler(data); + } catch (exception, stack) { + FlutterError.reportError(FlutterErrorDetails( + exception: exception, + stack: stack, + library: 'services library', + context: ErrorDescription('during a platform message callback'), + )); + } finally { + callback(response); + } + } + + @override + Future send(String channel, ByteData message) { + final MessageHandler handler = _mockHandlers[channel]; + if (handler != null) + return handler(message); + return _sendPlatformMessage(channel, message); + } + + @override + void setMessageHandler(String channel, MessageHandler handler) { + if (handler == null) + _handlers.remove(channel); + else + _handlers[channel] = handler; + } + + @override + void setMockMessageHandler(String channel, MessageHandler handler) { + if (handler == null) + _mockHandlers.remove(channel); + else + _mockHandlers[channel] = handler; + } +} diff --git a/packages/flutter/lib/src/services/platform_channel.dart b/packages/flutter/lib/src/services/platform_channel.dart index b5f98ea484..203c78775f 100644 --- a/packages/flutter/lib/src/services/platform_channel.dart +++ b/packages/flutter/lib/src/services/platform_channel.dart @@ -8,6 +8,7 @@ import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'binary_messenger.dart'; +import 'binding.dart'; import 'message_codec.dart'; import 'message_codecs.dart'; @@ -31,11 +32,12 @@ import 'message_codecs.dart'; class BasicMessageChannel { /// Creates a [BasicMessageChannel] with the specified [name], [codec] and [binaryMessenger]. /// - /// None of [name], [codec], or [binaryMessenger] may be null. - const BasicMessageChannel(this.name, this.codec, { this.binaryMessenger = defaultBinaryMessenger }) - : assert(name != null), - assert(codec != null), - assert(binaryMessenger != null); + /// The [name] and [codec] arguments cannot be null. The default [ServicesBinding.defaultBinaryMessenger] + /// instance is used if [binaryMessenger] is null. + const BasicMessageChannel(this.name, this.codec, { BinaryMessenger binaryMessenger }) + : assert(name != null), + assert(codec != null), + _binaryMessenger = binaryMessenger; /// The logical channel on which communication happens, not null. final String name; @@ -44,7 +46,8 @@ class BasicMessageChannel { final MessageCodec codec; /// The messenger which sends the bytes for this channel, not null. - final BinaryMessenger binaryMessenger; + BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger; + final BinaryMessenger _binaryMessenger; /// Sends the specified [message] to the platform plugins on this channel. /// @@ -118,11 +121,12 @@ class MethodChannel { /// The [codec] used will be [StandardMethodCodec], unless otherwise /// specified. /// - /// None of [name], [binaryMessenger], or [codec] may be null. - const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), this.binaryMessenger = defaultBinaryMessenger ]) - : assert(name != null), - assert(binaryMessenger != null), - assert(codec != null); + /// The [name] and [codec] arguments cannot be null. The default [ServicesBinding.defaultBinaryMessenger] + /// instance is used if [binaryMessenger] is null. + const MethodChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger ]) + : assert(name != null), + assert(codec != null), + _binaryMessenger = binaryMessenger; /// The logical channel on which communication happens, not null. final String name; @@ -130,10 +134,9 @@ class MethodChannel { /// The message codec used by this channel, not null. final MethodCodec codec; - /// The messenger used by this channel to send platform messages. - /// - /// The messenger may not be null. - final BinaryMessenger binaryMessenger; + /// The messenger used by this channel to send platform messages, not null. + BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger; + final BinaryMessenger _binaryMessenger; /// Invokes a [method] on this channel with the specified [arguments]. /// @@ -462,8 +465,12 @@ class EventChannel { /// The [codec] used will be [StandardMethodCodec], unless otherwise /// specified. /// - /// Neither [name] nor [codec] may be null. - const EventChannel(this.name, [this.codec = const StandardMethodCodec()]); + /// Neither [name] nor [codec] may be null. The default [ServicesBinding.defaultBinaryMessenger] + /// instance is used if [binaryMessenger] is null. + const EventChannel(this.name, [this.codec = const StandardMethodCodec(), BinaryMessenger binaryMessenger]) + : assert(name != null), + assert(codec != null), + _binaryMessenger = binaryMessenger; /// The logical channel on which communication happens, not null. final String name; @@ -471,6 +478,10 @@ class EventChannel { /// The message codec used by this channel, not null. final MethodCodec codec; + /// The messenger used by this channel to send platform messages, not null. + BinaryMessenger get binaryMessenger => _binaryMessenger ?? ServicesBinding.instance.defaultBinaryMessenger; + final BinaryMessenger _binaryMessenger; + /// Sets up a broadcast stream for receiving events on this channel. /// /// Returns a broadcast [Stream] which emits events to listeners as follows: @@ -488,7 +499,7 @@ class EventChannel { final MethodChannel methodChannel = MethodChannel(name, codec); StreamController controller; controller = StreamController.broadcast(onListen: () async { - defaultBinaryMessenger.setMessageHandler(name, (ByteData reply) async { + binaryMessenger.setMessageHandler(name, (ByteData reply) async { if (reply == null) { controller.close(); } else { @@ -511,7 +522,7 @@ class EventChannel { )); } }, onCancel: () async { - defaultBinaryMessenger.setMessageHandler(name, null); + binaryMessenger.setMessageHandler(name, null); try { await methodChannel.invokeMethod('cancel', arguments); } catch (exception, stack) { diff --git a/packages/flutter/lib/src/services/platform_messages.dart b/packages/flutter/lib/src/services/platform_messages.dart index 43fa779f89..7d9a88fa25 100644 --- a/packages/flutter/lib/src/services/platform_messages.dart +++ b/packages/flutter/lib/src/services/platform_messages.dart @@ -31,6 +31,9 @@ import 'platform_channel.dart'; class BinaryMessages { BinaryMessages._(); + /// The messenger which sends the platform messages, not null. + static final BinaryMessenger _binaryMessenger = ServicesBinding.instance.defaultBinaryMessenger; + /// Calls the handler registered for the given channel. /// /// Typically called by [ServicesBinding] to handle platform messages received @@ -43,7 +46,7 @@ class BinaryMessages { ByteData data, ui.PlatformMessageResponseCallback callback, ) { - return defaultBinaryMessenger.handlePlatformMessage(channel, data, callback); + return _binaryMessenger.handlePlatformMessage(channel, data, callback); } /// Send a binary message to the platform plugins on the given channel. @@ -52,7 +55,7 @@ class BinaryMessages { /// binary form. @Deprecated('Use defaultBinaryMessenger.send instead.') static Future send(String channel, ByteData message) { - return defaultBinaryMessenger.send(channel, message); + return _binaryMessenger.send(channel, message); } /// Set a callback for receiving messages from the platform plugins on the @@ -65,7 +68,7 @@ class BinaryMessages { /// The handler's return value, if non-null, is sent as a response, unencoded. @Deprecated('Use defaultBinaryMessenger.setMessageHandler instead.') static void setMessageHandler(String channel, Future handler(ByteData message)) { - defaultBinaryMessenger.setMessageHandler(channel, handler); + _binaryMessenger.setMessageHandler(channel, handler); } /// Set a mock callback for intercepting messages from the `send*` methods on @@ -81,6 +84,6 @@ class BinaryMessages { /// sent to platform plugins. @Deprecated('Use defaultBinaryMessenger.setMockMessageHandler instead.') static void setMockMessageHandler(String channel, Future handler(ByteData message)) { - defaultBinaryMessenger.setMockMessageHandler(channel, handler); + _binaryMessenger.setMockMessageHandler(channel, handler); } } diff --git a/packages/flutter/lib/src/widgets/binding.dart b/packages/flutter/lib/src/widgets/binding.dart index 5dc28c313d..fec0caf973 100644 --- a/packages/flutter/lib/src/widgets/binding.dart +++ b/packages/flutter/lib/src/widgets/binding.dart @@ -251,6 +251,10 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB void initInstances() { super.initInstances(); _instance = this; + // Initialization of [_buildOwner] has to be done after + // [super.initInstances] is called, as it requires [ServicesBinding] to + // properly setup the [defaultBinaryMessenger] instance. + _buildOwner = BuildOwner(); buildOwner.onBuildScheduled = _handleBuildScheduled; window.onLocaleChanged = handleLocaleChanged; window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged; @@ -371,7 +375,10 @@ mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererB /// The [BuildOwner] in charge of executing the build pipeline for the /// widget tree rooted at this binding. BuildOwner get buildOwner => _buildOwner; - final BuildOwner _buildOwner = BuildOwner(); + // Initialization of [_buildOwner] has to be done within the [initInstances] + // method, as it requires [ServicesBinding] to properly setup the + // [defaultBinaryMessenger] instance. + BuildOwner _buildOwner; /// The object in charge of the focus tree. /// diff --git a/packages/flutter/test/cupertino/text_field_test.dart b/packages/flutter/test/cupertino/text_field_test.dart index ba64f11f42..732c3f0e0e 100644 --- a/packages/flutter/test/cupertino/text_field_test.dart +++ b/packages/flutter/test/cupertino/text_field_test.dart @@ -125,6 +125,7 @@ class PathPointsMatcher extends Matcher { void main() { + TestWidgetsFlutterBinding.ensureInitialized(); final MockClipboard mockClipboard = MockClipboard(); SystemChannels.platform.setMockMethodCallHandler(mockClipboard.handleMethodCall); diff --git a/packages/flutter/test/foundation/service_extensions_test.dart b/packages/flutter/test/foundation/service_extensions_test.dart index 52a270f138..efa504af67 100644 --- a/packages/flutter/test/foundation/service_extensions_test.dart +++ b/packages/flutter/test/foundation/service_extensions_test.dart @@ -429,7 +429,7 @@ void main() { bool completed; completed = false; - defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData message) async { + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData message) async { expect(utf8.decode(message.buffer.asUint8List()), 'test'); completed = true; return ByteData(5); // 0x0000000000 @@ -448,7 +448,7 @@ void main() { data = await rootBundle.loadStructuredData('test', (String value) async { expect(value, '\x00\x00\x00\x00\x00'); return false; }); expect(data, isFalse); expect(completed, isTrue); - defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null); + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', null); }); test('Service extensions - exit', () async { diff --git a/packages/flutter/test/material/search_test.dart b/packages/flutter/test/material/search_test.dart index af3ae89dc3..297d1c1c5c 100644 --- a/packages/flutter/test/material/search_test.dart +++ b/packages/flutter/test/material/search_test.dart @@ -72,7 +72,7 @@ void main() { // Simulate system back button final ByteData message = const JSONMethodCodec().encodeMethodCall(const MethodCall('popRoute')); - await defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { }); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { }); await tester.pumpAndSettle(); expect(selectedResults, [null]); diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 2d72f6e469..da72c9a2b4 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -129,6 +129,7 @@ double getOpacity(WidgetTester tester, Finder finder) { } void main() { + TestWidgetsFlutterBinding.ensureInitialized(); final MockClipboard mockClipboard = MockClipboard(); SystemChannels.platform.setMockMethodCallHandler(mockClipboard.handleMethodCall); @@ -3391,7 +3392,7 @@ void main() { }); void sendFakeKeyEvent(Map data) { - defaultBinaryMessenger.handlePlatformMessage( + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( SystemChannels.keyEvent.name, SystemChannels.keyEvent.codec.encodeMessage(data), (ByteData data) { }, diff --git a/packages/flutter/test/scheduler/ticker_test.dart b/packages/flutter/test/scheduler/ticker_test.dart index 1f16b8193f..0aa3cd3b70 100644 --- a/packages/flutter/test/scheduler/ticker_test.dart +++ b/packages/flutter/test/scheduler/ticker_test.dart @@ -129,7 +129,7 @@ void main() { expect(tickCount, equals(0)); final ByteData message = const StringCodec().encodeMessage('AppLifecycleState.paused'); - await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { }); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', message, (_) { }); expect(ticker.isTicking, isFalse); expect(ticker.isActive, isTrue); @@ -138,7 +138,7 @@ void main() { testWidgets('Ticker can be created before application unpauses', (WidgetTester tester) async { final ByteData pausedMessage = const StringCodec().encodeMessage('AppLifecycleState.paused'); - await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', pausedMessage, (_) { }); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', pausedMessage, (_) { }); int tickCount = 0; void handleTick(Duration duration) { @@ -157,7 +157,7 @@ void main() { expect(ticker.isTicking, isFalse); final ByteData resumedMessage = const StringCodec().encodeMessage('AppLifecycleState.resumed'); - await defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', resumedMessage, (_) { }); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/lifecycle', resumedMessage, (_) { }); await tester.pump(const Duration(milliseconds: 10)); diff --git a/packages/flutter/test/semantics/semantics_service_test.dart b/packages/flutter/test/semantics/semantics_service_test.dart index 57ddf4416d..37d46c42ff 100644 --- a/packages/flutter/test/semantics/semantics_service_test.dart +++ b/packages/flutter/test/semantics/semantics_service_test.dart @@ -6,10 +6,15 @@ import 'dart:ui' show TextDirection; import 'package:flutter/semantics.dart'; import 'package:flutter/services.dart' show SystemChannels; +import 'package:flutter_test/flutter_test.dart' show TestWidgetsFlutterBinding; import '../flutter_test_alternative.dart'; void main() { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + test('Semantic announcement', () async { final List> log = >[]; diff --git a/packages/flutter/test/services/fake_platform_views.dart b/packages/flutter/test/services/fake_platform_views.dart index d25783bece..0df472ff7d 100644 --- a/packages/flutter/test/services/fake_platform_views.dart +++ b/packages/flutter/test/services/fake_platform_views.dart @@ -63,7 +63,7 @@ class FakeAndroidPlatformViewsController { void invokeViewFocused(int viewId) { final MethodCodec codec = SystemChannels.platform_views.codec; final ByteData data = codec.encodeMethodCall(MethodCall('viewFocused', viewId)); - defaultBinaryMessenger.handlePlatformMessage(SystemChannels.platform_views.name, data, (ByteData data) {}); + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage(SystemChannels.platform_views.name, data, (ByteData data) {}); } Future _onMethodCall(MethodCall call) { diff --git a/packages/flutter/test/services/haptic_feedback_test.dart b/packages/flutter/test/services/haptic_feedback_test.dart index 9a01f8e436..8abbfc9596 100644 --- a/packages/flutter/test/services/haptic_feedback_test.dart +++ b/packages/flutter/test/services/haptic_feedback_test.dart @@ -6,6 +6,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + test('Haptic feedback control test', () async { final List log = []; diff --git a/packages/flutter/test/services/platform_channel_test.dart b/packages/flutter/test/services/platform_channel_test.dart index 8e7586a69e..b18b08d10d 100644 --- a/packages/flutter/test/services/platform_channel_test.dart +++ b/packages/flutter/test/services/platform_channel_test.dart @@ -6,14 +6,20 @@ import 'dart:async'; import 'dart:typed_data'; import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart' show TestWidgetsFlutterBinding; + import '../flutter_test_alternative.dart'; void main() { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + group('BasicMessageChannel', () { const MessageCodec string = StringCodec(); const BasicMessageChannel channel = BasicMessageChannel('ch', string); test('can send string message and get reply', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch', (ByteData message) async => string.encodeMessage(string.decodeMessage(message) + ' world'), ); @@ -23,7 +29,7 @@ void main() { test('can receive string message and send reply', () async { channel.setMessageHandler((String message) async => message + ' world'); String reply; - await defaultBinaryMessenger.handlePlatformMessage( + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'ch', const StringCodec().encodeMessage('hello'), (ByteData replyBinary) { @@ -39,7 +45,7 @@ void main() { const MethodCodec jsonMethod = JSONMethodCodec(); const MethodChannel channel = MethodChannel('ch7', jsonMethod); test('can invoke method and get result', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); @@ -54,7 +60,7 @@ void main() { expect(result, equals('hello world')); }); test('can invoke list method and get result', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); @@ -70,7 +76,7 @@ void main() { }); test('can invoke list method and get null result', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); @@ -86,7 +92,7 @@ void main() { test('can invoke map method and get result', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); @@ -102,7 +108,7 @@ void main() { }); test('can invoke map method and get null result', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); @@ -117,7 +123,7 @@ void main() { }); test('can invoke method and get error', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async { return jsonMessage.encodeMessage([ @@ -139,7 +145,7 @@ void main() { } }); test('can invoke unimplemented method', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch7', (ByteData message) async => null, ); @@ -157,7 +163,7 @@ void main() { channel.setMethodCallHandler(null); final ByteData call = jsonMethod.encodeMethodCall(const MethodCall('sayHello', 'hello')); ByteData envelope; - await defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { envelope = result; }); expect(envelope, isNull); @@ -168,7 +174,7 @@ void main() { }); final ByteData call = jsonMethod.encodeMethodCall(const MethodCall('sayHello', 'hello')); ByteData envelope; - await defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { envelope = result; }); expect(envelope, isNull); @@ -177,7 +183,7 @@ void main() { channel.setMethodCallHandler((MethodCall call) async => '${call.arguments}, world'); final ByteData call = jsonMethod.encodeMethodCall(const MethodCall('sayHello', 'hello')); ByteData envelope; - await defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { envelope = result; }); expect(jsonMethod.decodeEnvelope(envelope), equals('hello, world')); @@ -188,7 +194,7 @@ void main() { }); final ByteData call = jsonMethod.encodeMethodCall(const MethodCall('sayHello', 'hello')); ByteData envelope; - await defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { envelope = result; }); try { @@ -207,7 +213,7 @@ void main() { }); final ByteData call = jsonMethod.encodeMethodCall(const MethodCall('sayHello', 'hello')); ByteData envelope; - await defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('ch7', call, (ByteData result) { envelope = result; }); try { @@ -226,7 +232,7 @@ void main() { const MethodCodec jsonMethod = JSONMethodCodec(); const EventChannel channel = EventChannel('ch', jsonMethod); void emitEvent(dynamic event) { - defaultBinaryMessenger.handlePlatformMessage( + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( 'ch', event, (ByteData reply) { }, @@ -234,7 +240,7 @@ void main() { } test('can receive event stream', () async { bool canceled = false; - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); @@ -258,7 +264,7 @@ void main() { expect(canceled, isTrue); }); test('can receive error event', () async { - defaultBinaryMessenger.setMockMessageHandler( + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler( 'ch', (ByteData message) async { final Map methodCall = jsonMessage.decodeMessage(message); diff --git a/packages/flutter/test/services/platform_messages_test.dart b/packages/flutter/test/services/platform_messages_test.dart index fcf7f5a2e6..ccbc951dd8 100644 --- a/packages/flutter/test/services/platform_messages_test.dart +++ b/packages/flutter/test/services/platform_messages_test.dart @@ -16,18 +16,18 @@ void main() { final List log = []; - defaultBinaryMessenger.setMockMessageHandler('test1', (ByteData message) async { + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('test1', (ByteData message) async { log.add(message); return null; }); final ByteData message = ByteData(2)..setUint16(0, 0xABCD); - await defaultBinaryMessenger.send('test1', message); + await ServicesBinding.instance.defaultBinaryMessenger.send('test1', message); expect(log, equals([message])); log.clear(); - defaultBinaryMessenger.setMockMessageHandler('test1', null); - await defaultBinaryMessenger.send('test1', message); + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('test1', null); + await ServicesBinding.instance.defaultBinaryMessenger.send('test1', message); expect(log, isEmpty); }); } diff --git a/packages/flutter/test/services/platform_views_test.dart b/packages/flutter/test/services/platform_views_test.dart index eeccd0ce29..31bd1c90dc 100644 --- a/packages/flutter/test/services/platform_views_test.dart +++ b/packages/flutter/test/services/platform_views_test.dart @@ -4,12 +4,17 @@ import 'package:flutter/painting.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart' show TestWidgetsFlutterBinding; import '../flutter_test_alternative.dart'; import 'fake_platform_views.dart'; void main() { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + group('Android', () { FakeAndroidPlatformViewsController viewsController; setUp(() { diff --git a/packages/flutter/test/services/system_chrome_test.dart b/packages/flutter/test/services/system_chrome_test.dart index ac02009a31..c1810ad1e6 100644 --- a/packages/flutter/test/services/system_chrome_test.dart +++ b/packages/flutter/test/services/system_chrome_test.dart @@ -61,7 +61,7 @@ void main() { test('setApplicationSwitcherDescription missing plugin', () async { final List log = []; - defaultBinaryMessenger.setMockMessageHandler('flutter/platform', (ByteData message) { + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/platform', (ByteData message) { log.add(message); return null; }); diff --git a/packages/flutter/test/services/system_navigator_test.dart b/packages/flutter/test/services/system_navigator_test.dart index f3e28fa4fc..85ab1e208d 100644 --- a/packages/flutter/test/services/system_navigator_test.dart +++ b/packages/flutter/test/services/system_navigator_test.dart @@ -6,6 +6,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + test('System navigator control test', () async { final List log = []; diff --git a/packages/flutter/test/services/system_sound_test.dart b/packages/flutter/test/services/system_sound_test.dart index c9fcf54998..9ddc846d33 100644 --- a/packages/flutter/test/services/system_sound_test.dart +++ b/packages/flutter/test/services/system_sound_test.dart @@ -6,6 +6,10 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { + setUp(() { + TestWidgetsFlutterBinding.ensureInitialized(); + }); + test('System sound control test', () async { final List log = []; diff --git a/packages/flutter/test/widgets/binding_test.dart b/packages/flutter/test/widgets/binding_test.dart index 4991e9e350..e1ed3f647f 100644 --- a/packages/flutter/test/widgets/binding_test.dart +++ b/packages/flutter/test/widgets/binding_test.dart @@ -46,12 +46,13 @@ void main() { WidgetsBinding.instance.addObserver(observer); final ByteData message = const JSONMessageCodec().encodeMessage( {'type': 'memoryPressure'}); - await defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { }); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/system', message, (_) { }); expect(observer.sawMemoryPressure, true); WidgetsBinding.instance.removeObserver(observer); }); testWidgets('handleLifecycleStateChanged callback', (WidgetTester tester) async { + final BinaryMessenger defaultBinaryMessenger = ServicesBinding.instance.defaultBinaryMessenger; final AppLifecycleStateObserver observer = AppLifecycleStateObserver(); WidgetsBinding.instance.addObserver(observer); @@ -79,13 +80,14 @@ void main() { const String testRouteName = 'testRouteName'; final ByteData message = const JSONMethodCodec().encodeMethodCall( const MethodCall('pushRoute', testRouteName)); - await defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { }); + await ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage('flutter/navigation', message, (_) { }); expect(observer.pushedRoute, testRouteName); WidgetsBinding.instance.removeObserver(observer); }); testWidgets('Application lifecycle affects frame scheduling', (WidgetTester tester) async { + final BinaryMessenger defaultBinaryMessenger = ServicesBinding.instance.defaultBinaryMessenger; ByteData message; expect(tester.binding.hasScheduledFrame, isFalse); diff --git a/packages/flutter/test/widgets/focus_manager_test.dart b/packages/flutter/test/widgets/focus_manager_test.dart index 7bb4033b5b..482e3269a5 100644 --- a/packages/flutter/test/widgets/focus_manager_test.dart +++ b/packages/flutter/test/widgets/focus_manager_test.dart @@ -10,7 +10,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; void sendFakeKeyEvent(Map data) { - defaultBinaryMessenger.handlePlatformMessage( + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( SystemChannels.keyEvent.name, SystemChannels.keyEvent.codec.encodeMessage(data), (ByteData data) {}, diff --git a/packages/flutter/test/widgets/raw_keyboard_listener_test.dart b/packages/flutter/test/widgets/raw_keyboard_listener_test.dart index 41cd849331..2f67cfb9b5 100644 --- a/packages/flutter/test/widgets/raw_keyboard_listener_test.dart +++ b/packages/flutter/test/widgets/raw_keyboard_listener_test.dart @@ -9,7 +9,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; void sendFakeKeyEvent(Map data) { - defaultBinaryMessenger.handlePlatformMessage( + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( SystemChannels.keyEvent.name, SystemChannels.keyEvent.codec.encodeMessage(data), (ByteData data) {}, diff --git a/packages/flutter/test/widgets/selectable_text_test.dart b/packages/flutter/test/widgets/selectable_text_test.dart index 5f4f139396..0087b37a8e 100644 --- a/packages/flutter/test/widgets/selectable_text_test.dart +++ b/packages/flutter/test/widgets/selectable_text_test.dart @@ -122,6 +122,7 @@ double getOpacity(WidgetTester tester, Finder finder) { } void main() { + TestWidgetsFlutterBinding.ensureInitialized(); final MockClipboard mockClipboard = MockClipboard(); SystemChannels.platform.setMockMethodCallHandler(mockClipboard.handleMethodCall); @@ -1247,7 +1248,7 @@ void main() { }); void sendFakeKeyEvent(Map data) { - defaultBinaryMessenger.handlePlatformMessage( + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( SystemChannels.keyEvent.name, SystemChannels.keyEvent.codec.encodeMessage(data), (ByteData data) { }, diff --git a/packages/flutter/test/widgets/shortcuts_test.dart b/packages/flutter/test/widgets/shortcuts_test.dart index 0b30585440..930212f82c 100644 --- a/packages/flutter/test/widgets/shortcuts_test.dart +++ b/packages/flutter/test/widgets/shortcuts_test.dart @@ -11,7 +11,7 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; void sendFakeKeyEvent(Map data) { - defaultBinaryMessenger.handlePlatformMessage( + ServicesBinding.instance.defaultBinaryMessenger.handlePlatformMessage( SystemChannels.keyEvent.name, SystemChannels.keyEvent.codec.encodeMessage(data), (ByteData data) {}, diff --git a/packages/flutter_test/lib/src/binding.dart b/packages/flutter_test/lib/src/binding.dart index dbe67e9d33..c783c8547f 100644 --- a/packages/flutter_test/lib/src/binding.dart +++ b/packages/flutter_test/lib/src/binding.dart @@ -207,10 +207,10 @@ abstract class TestWidgetsFlutterBinding extends BindingBase @override void initInstances() { + super.initInstances(); timeDilation = 1.0; // just in case the developer has artificially changed it for development HttpOverrides.global = _MockHttpOverrides(); _testTextInput = TestTextInput(onCleared: _resetFocusedEditable)..register(); - super.initInstances(); } @override @@ -808,7 +808,7 @@ class AutomatedTestWidgetsFlutterBinding extends TestWidgetsFlutterBinding { final String assetFolderPath = Platform.environment['UNIT_TEST_ASSETS']; final String prefix = 'packages/${Platform.environment['APP_NAME']}/'; - defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData message) { + ServicesBinding.instance.defaultBinaryMessenger.setMockMessageHandler('flutter/assets', (ByteData message) { String key = utf8.decode(message.buffer.asUint8List()); File asset = File(path.join(assetFolderPath, key)); diff --git a/packages/flutter_test/lib/src/test_text_input.dart b/packages/flutter_test/lib/src/test_text_input.dart index 9c663df4a0..00b0eaf1f3 100644 --- a/packages/flutter_test/lib/src/test_text_input.dart +++ b/packages/flutter_test/lib/src/test_text_input.dart @@ -36,6 +36,9 @@ class TestTextInput { /// first be requested, e.g. using [WidgetTester.showKeyboard]. final VoidCallback onCleared; + /// The messenger which sends the bytes for this channel, not null. + BinaryMessenger get _binaryMessenger => ServicesBinding.instance.defaultBinaryMessenger; + /// Installs this object as a mock handler for [SystemChannels.textInput]. void register() { SystemChannels.textInput.setMockMethodCallHandler(_handleTextInputCall); @@ -108,7 +111,7 @@ class TestTextInput { // test this code does not run in a package:test test zone. if (_client == 0) throw TestFailure('Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.'); - defaultBinaryMessenger.handlePlatformMessage( + _binaryMessenger.handlePlatformMessage( SystemChannels.textInput.name, SystemChannels.textInput.codec.encodeMethodCall( MethodCall( @@ -140,7 +143,7 @@ class TestTextInput { final Completer completer = Completer(); - defaultBinaryMessenger.handlePlatformMessage( + _binaryMessenger.handlePlatformMessage( SystemChannels.textInput.name, SystemChannels.textInput.codec.encodeMethodCall( MethodCall(