Unverified Commit 83af6f48 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Adds a type parameter to invokeMethod (and additional utility methods) (#26303)

parent dca8d36d
...@@ -26,7 +26,7 @@ Future<String> dataHandler(String message) async { ...@@ -26,7 +26,7 @@ Future<String> dataHandler(String message) async {
final Completer<String> completer = Completer<String>(); final Completer<String> completer = Completer<String>();
final int id = int.tryParse(message.split('#')[1]) ?? 0; final int id = int.tryParse(message.split('#')[1]) ?? 0;
Future<void> completeSemantics([Object _]) async { Future<void> completeSemantics([Object _]) async {
final dynamic result = await kSemanticsChannel.invokeMethod('getSemanticsNode', <String, dynamic>{ final dynamic result = await kSemanticsChannel.invokeMethod<dynamic>('getSemanticsNode', <String, dynamic>{
'id': id, 'id': id,
}); });
completer.complete(json.encode(result)); completer.complete(json.encode(result));
......
...@@ -125,15 +125,15 @@ class PlatformViewState extends State<PlatformViewPage> { ...@@ -125,15 +125,15 @@ class PlatformViewState extends State<PlatformViewPage> {
.cast<Map<dynamic, dynamic>>() .cast<Map<dynamic, dynamic>>()
.map<Map<String, dynamic>>((Map<dynamic, dynamic> e) =>e.cast<String, dynamic>()) .map<Map<String, dynamic>>((Map<dynamic, dynamic> e) =>e.cast<String, dynamic>())
.toList(); .toList();
await channel.invokeMethod('pipeFlutterViewEvents'); await channel.invokeMethod<void>('pipeFlutterViewEvents');
await viewChannel.invokeMethod('pipeTouchEvents'); await viewChannel.invokeMethod<void>('pipeTouchEvents');
print('replaying ${recordedEvents.length} motion events'); print('replaying ${recordedEvents.length} motion events');
for (Map<String, dynamic> event in recordedEvents.reversed) { for (Map<String, dynamic> event in recordedEvents.reversed) {
await channel.invokeMethod('synthesizeEvent', event); await channel.invokeMethod<void>('synthesizeEvent', event);
} }
await channel.invokeMethod('stopFlutterViewEvents'); await channel.invokeMethod<void>('stopFlutterViewEvents');
await viewChannel.invokeMethod('stopTouchEvents'); await viewChannel.invokeMethod<void>('stopTouchEvents');
if (flutterViewEvents.length != embeddedViewEvents.length) if (flutterViewEvents.length != embeddedViewEvents.length)
return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events'; return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events';
...@@ -160,7 +160,7 @@ class PlatformViewState extends State<PlatformViewPage> { ...@@ -160,7 +160,7 @@ class PlatformViewState extends State<PlatformViewPage> {
} }
Future<void> saveRecordedEvents(ByteData data, BuildContext context) async { Future<void> saveRecordedEvents(ByteData data, BuildContext context) async {
if (!await channel.invokeMethod('getStoragePermission')) { if (!await channel.invokeMethod<bool>('getStoragePermission')) {
showMessage( showMessage(
context, 'External storage permissions are required to save events'); context, 'External storage permissions are required to save events');
return; return;
...@@ -190,11 +190,11 @@ class PlatformViewState extends State<PlatformViewPage> { ...@@ -190,11 +190,11 @@ class PlatformViewState extends State<PlatformViewPage> {
} }
void listenToFlutterViewEvents() { void listenToFlutterViewEvents() {
channel.invokeMethod('pipeFlutterViewEvents'); channel.invokeMethod<void>('pipeFlutterViewEvents');
viewChannel.invokeMethod('pipeTouchEvents'); viewChannel.invokeMethod<void>('pipeTouchEvents');
Timer(const Duration(seconds: 3), () { Timer(const Duration(seconds: 3), () {
channel.invokeMethod('stopFlutterViewEvents'); channel.invokeMethod<void>('stopFlutterViewEvents');
viewChannel.invokeMethod('stopTouchEvents'); viewChannel.invokeMethod<void>('stopTouchEvents');
}); });
} }
......
...@@ -67,7 +67,7 @@ Future<TestStepResult> _methodCallSuccessHandshake( ...@@ -67,7 +67,7 @@ Future<TestStepResult> _methodCallSuccessHandshake(
dynamic result = nothing; dynamic result = nothing;
dynamic error = nothing; dynamic error = nothing;
try { try {
result = await channel.invokeMethod('success', arguments); result = await channel.invokeMethod<dynamic>('success', arguments);
} catch (e) { } catch (e) {
error = e; error = e;
} }
...@@ -95,7 +95,7 @@ Future<TestStepResult> _methodCallErrorHandshake( ...@@ -95,7 +95,7 @@ Future<TestStepResult> _methodCallErrorHandshake(
dynamic errorDetails = nothing; dynamic errorDetails = nothing;
dynamic error = nothing; dynamic error = nothing;
try { try {
error = await channel.invokeMethod('error', arguments); error = await channel.invokeMethod<dynamic>('error', arguments);
} on PlatformException catch (e) { } on PlatformException catch (e) {
errorDetails = e.details; errorDetails = e.details;
} catch (e) { } catch (e) {
...@@ -123,7 +123,7 @@ Future<TestStepResult> _methodCallNotImplementedHandshake( ...@@ -123,7 +123,7 @@ Future<TestStepResult> _methodCallNotImplementedHandshake(
dynamic result = nothing; dynamic result = nothing;
dynamic error = nothing; dynamic error = nothing;
try { try {
error = await channel.invokeMethod('notImplemented'); error = await channel.invokeMethod<dynamic>('notImplemented');
} on MissingPluginException { } on MissingPluginException {
result = null; result = null;
} catch (e) { } catch (e) {
......
...@@ -47,11 +47,11 @@ Widget builds: $_widgetBuilds'''; ...@@ -47,11 +47,11 @@ Widget builds: $_widgetBuilds''';
_summary = 'Producing texture frames at .5x speed...'; _summary = 'Producing texture frames at .5x speed...';
_state = FrameState.slow; _state = FrameState.slow;
_icon = Icons.stop; _icon = Icons.stop;
channel.invokeMethod('start', _flutterFrameRate ~/ 2); channel.invokeMethod<void>('start', _flutterFrameRate ~/ 2);
break; break;
case FrameState.slow: case FrameState.slow:
debugPrint('Stopping .5x speed test...'); debugPrint('Stopping .5x speed test...');
await channel.invokeMethod('stop'); await channel.invokeMethod<void>('stop');
await _summarizeStats(); await _summarizeStats();
_icon = Icons.fast_forward; _icon = Icons.fast_forward;
_state = FrameState.afterSlow; _state = FrameState.afterSlow;
...@@ -62,11 +62,11 @@ Widget builds: $_widgetBuilds'''; ...@@ -62,11 +62,11 @@ Widget builds: $_widgetBuilds''';
_summary = 'Producing texture frames at 2x speed...'; _summary = 'Producing texture frames at 2x speed...';
_state = FrameState.fast; _state = FrameState.fast;
_icon = Icons.stop; _icon = Icons.stop;
channel.invokeMethod('start', (_flutterFrameRate * 2).toInt()); channel.invokeMethod<void>('start', (_flutterFrameRate * 2).toInt());
break; break;
case FrameState.fast: case FrameState.fast:
debugPrint('Stopping 2x speed test...'); debugPrint('Stopping 2x speed test...');
await channel.invokeMethod('stop'); await channel.invokeMethod<void>('stop');
await _summarizeStats(); await _summarizeStats();
_state = FrameState.afterFast; _state = FrameState.afterFast;
_icon = Icons.replay; _icon = Icons.replay;
......
...@@ -18,7 +18,7 @@ class _FlavorState extends State<Flavor> { ...@@ -18,7 +18,7 @@ class _FlavorState extends State<Flavor> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
const MethodChannel('flavor').invokeMethod('getFlavor').then((Object flavor) { const MethodChannel('flavor').invokeMethod<String>('getFlavor').then((String flavor) {
setState(() { setState(() {
_flavor = flavor; _flavor = flavor;
}); });
......
...@@ -82,10 +82,10 @@ Future<void> main() async { ...@@ -82,10 +82,10 @@ Future<void> main() async {
await controller.tap(find.byTooltip('Back')); await controller.tap(find.byTooltip('Back'));
} }
print('Finished successfully!'); print('Finished successfully!');
_kTestChannel.invokeMethod('success'); _kTestChannel.invokeMethod<void>('success');
} catch (error, stack) { } catch (error, stack) {
print('Caught error: $error\n$stack'); print('Caught error: $error\n$stack');
_kTestChannel.invokeMethod('failure'); _kTestChannel.invokeMethod<void>('failure');
} }
} }
......
...@@ -34,7 +34,7 @@ class Clipboard { ...@@ -34,7 +34,7 @@ class Clipboard {
/// Stores the given clipboard data on the clipboard. /// Stores the given clipboard data on the clipboard.
static Future<void> setData(ClipboardData data) async { static Future<void> setData(ClipboardData data) async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'Clipboard.setData', 'Clipboard.setData',
<String, dynamic>{ <String, dynamic>{
'text': data.text, 'text': data.text,
......
...@@ -21,7 +21,7 @@ class HapticFeedback { ...@@ -21,7 +21,7 @@ class HapticFeedback {
/// On Android, this uses the platform haptic feedback API to simulate a /// On Android, this uses the platform haptic feedback API to simulate a
/// response to a long press (`HapticFeedbackConstants.LONG_PRESS`). /// response to a long press (`HapticFeedbackConstants.LONG_PRESS`).
static Future<void> vibrate() async { static Future<void> vibrate() async {
await SystemChannels.platform.invokeMethod('HapticFeedback.vibrate'); await SystemChannels.platform.invokeMethod<void>('HapticFeedback.vibrate');
} }
/// Provides a haptic feedback corresponding a collision impact with a light mass. /// Provides a haptic feedback corresponding a collision impact with a light mass.
...@@ -32,7 +32,7 @@ class HapticFeedback { ...@@ -32,7 +32,7 @@ class HapticFeedback {
/// ///
/// On Android, this uses `HapticFeedbackConstants.VIRTUAL_KEY`. /// On Android, this uses `HapticFeedbackConstants.VIRTUAL_KEY`.
static Future<void> lightImpact() async { static Future<void> lightImpact() async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'HapticFeedback.vibrate', 'HapticFeedback.vibrate',
'HapticFeedbackType.lightImpact', 'HapticFeedbackType.lightImpact',
); );
...@@ -46,7 +46,7 @@ class HapticFeedback { ...@@ -46,7 +46,7 @@ class HapticFeedback {
/// ///
/// On Android, this uses `HapticFeedbackConstants.KEYBOARD_TAP`. /// On Android, this uses `HapticFeedbackConstants.KEYBOARD_TAP`.
static Future<void> mediumImpact() async { static Future<void> mediumImpact() async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'HapticFeedback.vibrate', 'HapticFeedback.vibrate',
'HapticFeedbackType.mediumImpact', 'HapticFeedbackType.mediumImpact',
); );
...@@ -61,7 +61,7 @@ class HapticFeedback { ...@@ -61,7 +61,7 @@ class HapticFeedback {
/// On Android, this uses `HapticFeedbackConstants.CONTEXT_CLICK` on API levels /// On Android, this uses `HapticFeedbackConstants.CONTEXT_CLICK` on API levels
/// 23 and above. This call has no effects on Android API levels below 23. /// 23 and above. This call has no effects on Android API levels below 23.
static Future<void> heavyImpact() async { static Future<void> heavyImpact() async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'HapticFeedback.vibrate', 'HapticFeedback.vibrate',
'HapticFeedbackType.heavyImpact', 'HapticFeedbackType.heavyImpact',
); );
...@@ -74,7 +74,7 @@ class HapticFeedback { ...@@ -74,7 +74,7 @@ class HapticFeedback {
/// ///
/// On Android, this uses `HapticFeedbackConstants.CLOCK_TICK`. /// On Android, this uses `HapticFeedbackConstants.CLOCK_TICK`.
static Future<void> selectionClick() async { static Future<void> selectionClick() async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'HapticFeedback.vibrate', 'HapticFeedback.vibrate',
'HapticFeedbackType.selectionClick', 'HapticFeedbackType.selectionClick',
); );
......
...@@ -128,6 +128,12 @@ class MethodChannel { ...@@ -128,6 +128,12 @@ class MethodChannel {
/// result. The values supported by the default codec and their platform-specific /// result. The values supported by the default codec and their platform-specific
/// counterparts are documented with [StandardMessageCodec]. /// counterparts are documented with [StandardMessageCodec].
/// ///
/// The generic argument `T` of the method can be inferred by the surrounding
/// context, or provided explicitly. If it does not match the returned type of
/// the channel, a [TypeError] will be thrown at runtime. `T` cannot be a class
/// with generics other than `dynamic`. For example, `Map<String, String>`
/// is not supported but `Map<dynamic, dynamic>` or `Map` is.
///
/// Returns a [Future] which completes to one of the following: /// Returns a [Future] which completes to one of the following:
/// ///
/// * a result (possibly null), on successful invocation; /// * a result (possibly null), on successful invocation;
...@@ -149,10 +155,9 @@ class MethodChannel { ...@@ -149,10 +155,9 @@ class MethodChannel {
/// static const MethodChannel _channel = MethodChannel('music'); /// static const MethodChannel _channel = MethodChannel('music');
/// ///
/// static Future<bool> isLicensed() async { /// static Future<bool> isLicensed() async {
/// // invokeMethod returns a Future<dynamic>, and we cannot pass that for /// // invokeMethod returns a Future<T> which can be inferred as bool
/// // a Future<bool>, hence the indirection. /// // in this context.
/// final bool result = await _channel.invokeMethod('isLicensed'); /// return _channel.invokeMethod('isLicensed');
/// return result;
/// } /// }
/// ///
/// static Future<List<Song>> songs() async { /// static Future<List<Song>> songs() async {
...@@ -160,6 +165,7 @@ class MethodChannel { ...@@ -160,6 +165,7 @@ class MethodChannel {
/// // List<dynamic> with Map<dynamic, dynamic> entries. Post-processing /// // List<dynamic> with Map<dynamic, dynamic> entries. Post-processing
/// // code thus cannot assume e.g. List<Map<String, String>> even though /// // code thus cannot assume e.g. List<Map<String, String>> even though
/// // the actual values involved would support such a typed container. /// // the actual values involved would support such a typed container.
/// // The correct type cannot be inferred with any value of `T`.
/// final List<dynamic> songs = await _channel.invokeMethod('getSongs'); /// final List<dynamic> songs = await _channel.invokeMethod('getSongs');
/// return songs.map(Song.fromJson).toList(); /// return songs.map(Song.fromJson).toList();
/// } /// }
...@@ -168,7 +174,7 @@ class MethodChannel { ...@@ -168,7 +174,7 @@ class MethodChannel {
/// // Errors occurring on the platform side cause invokeMethod to throw /// // Errors occurring on the platform side cause invokeMethod to throw
/// // PlatformExceptions. /// // PlatformExceptions.
/// try { /// try {
/// await _channel.invokeMethod('play', <String, dynamic>{ /// return _channel.invokeMethod('play', <String, dynamic>{
/// 'song': song.id, /// 'song': song.id,
/// 'volume': volume, /// 'volume': volume,
/// }); /// });
...@@ -275,21 +281,54 @@ class MethodChannel { ...@@ -275,21 +281,54 @@ class MethodChannel {
/// ///
/// See also: /// See also:
/// ///
/// * [invokeListMethod], for automatically returning typed lists.
/// * [invokeMapMethod], for automatically returning typed maps.
/// * [StandardMessageCodec] which defines the payload values supported by /// * [StandardMessageCodec] which defines the payload values supported by
/// [StandardMethodCodec]. /// [StandardMethodCodec].
/// * [JSONMessageCodec] which defines the payload values supported by /// * [JSONMessageCodec] which defines the payload values supported by
/// [JSONMethodCodec]. /// [JSONMethodCodec].
/// * <https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodCall.html> /// * <https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodCall.html>
/// for how to access method call arguments on Android. /// for how to access method call arguments on Android.
Future<dynamic> invokeMethod(String method, [dynamic arguments]) async { @optionalTypeArgs
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
assert(method != null); assert(method != null);
final dynamic result = await BinaryMessages.send( final ByteData result = await BinaryMessages.send(
name, name,
codec.encodeMethodCall(MethodCall(method, arguments)), codec.encodeMethodCall(MethodCall(method, arguments)),
); );
if (result == null) if (result == null) {
throw MissingPluginException('No implementation found for method $method on channel $name'); throw MissingPluginException('No implementation found for method $method on channel $name');
return codec.decodeEnvelope(result); }
final T typedResult = codec.decodeEnvelope(result);
return typedResult;
}
/// An implementation of [invokeMethod] that can return typed lists.
///
/// Dart generics are reified, meaning that an untyped List<dynamic>
/// cannot masquerade as a List<T>. Since invokeMethod can only return
/// dynamic maps, we instead create a new typed list using [List.cast].
///
/// See also:
///
/// * [invokeMethod], which this call delegates to.
Future<List<T>> invokeListMethod<T>(String method, [dynamic arguments]) async {
final List<dynamic> result = await invokeMethod<List<dynamic>>(method, arguments);
return result.cast<T>();
}
/// An implementation of [invokeMethod] that can return typed maps.
///
/// Dart generics are reified, meaning that an untyped Map<dynamic, dynamic>
/// cannot masquerade as a Map<K, V>. Since invokeMethod can only return
/// dynamic maps, we instead create a new typed map using [Map.cast].
///
/// See also:
///
/// * [invokeMethod], which this call delegates to.
Future<Map<K, V>> invokeMapMethod<K, V>(String method, [dynamic arguments]) async {
final Map<dynamic, dynamic> result = await invokeMethod<Map<dynamic, dynamic>>(method, arguments);
return result.cast<K, V>();
} }
/// Sets a callback for receiving method calls on this channel. /// Sets a callback for receiving method calls on this channel.
...@@ -366,13 +405,27 @@ class OptionalMethodChannel extends MethodChannel { ...@@ -366,13 +405,27 @@ class OptionalMethodChannel extends MethodChannel {
: super(name, codec); : super(name, codec);
@override @override
Future<dynamic> invokeMethod(String method, [dynamic arguments]) async { Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
try { try {
return await super.invokeMethod(method, arguments); final T result = await super.invokeMethod<T>(method, arguments);
return result;
} on MissingPluginException { } on MissingPluginException {
return null; return null;
} }
} }
@override
Future<List<T>> invokeListMethod<T>(String method, [dynamic arguments]) async {
final List<dynamic> result = await invokeMethod<List<dynamic>>(method, arguments);
return result.cast<T>();
}
@override
Future<Map<K, V>> invokeMapMethod<K, V>(String method, [dynamic arguments]) async {
final Map<dynamic, dynamic> result = await invokeMethod<Map<dynamic, dynamic>>(method, arguments);
return result.cast<K, V>();
}
} }
/// A named channel for communicating with platform plugins using event streams. /// A named channel for communicating with platform plugins using event streams.
...@@ -434,7 +487,7 @@ class EventChannel { ...@@ -434,7 +487,7 @@ class EventChannel {
return null; return null;
}); });
try { try {
await methodChannel.invokeMethod('listen', arguments); await methodChannel.invokeMethod<void>('listen', arguments);
} catch (exception, stack) { } catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
exception: exception, exception: exception,
...@@ -446,7 +499,7 @@ class EventChannel { ...@@ -446,7 +499,7 @@ class EventChannel {
}, onCancel: () async { }, onCancel: () async {
BinaryMessages.setMessageHandler(name, null); BinaryMessages.setMessageHandler(name, null);
try { try {
await methodChannel.invokeMethod('cancel', arguments); await methodChannel.invokeMethod<void>('cancel', arguments);
} catch (exception, stack) { } catch (exception, stack) {
FlutterError.reportError(FlutterErrorDetails( FlutterError.reportError(FlutterErrorDetails(
exception: exception, exception: exception,
......
...@@ -129,7 +129,7 @@ class PlatformViewsService { ...@@ -129,7 +129,7 @@ class PlatformViewsService {
paramsByteData.lengthInBytes, paramsByteData.lengthInBytes,
); );
} }
await SystemChannels.platform_views.invokeMethod('create', args); await SystemChannels.platform_views.invokeMethod<void>('create', args);
return UiKitViewController._(id, layoutDirection); return UiKitViewController._(id, layoutDirection);
} }
} }
...@@ -485,7 +485,7 @@ class AndroidViewController { ...@@ -485,7 +485,7 @@ class AndroidViewController {
/// disposed. /// disposed.
Future<void> dispose() async { Future<void> dispose() async {
if (_state == _AndroidViewState.creating || _state == _AndroidViewState.created) if (_state == _AndroidViewState.creating || _state == _AndroidViewState.created)
await SystemChannels.platform_views.invokeMethod('dispose', id); await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
_state = _AndroidViewState.disposed; _state = _AndroidViewState.disposed;
} }
...@@ -504,7 +504,7 @@ class AndroidViewController { ...@@ -504,7 +504,7 @@ class AndroidViewController {
if (_state == _AndroidViewState.waitingForSize) if (_state == _AndroidViewState.waitingForSize)
return _create(size); return _create(size);
await SystemChannels.platform_views.invokeMethod('resize', <String, dynamic> { await SystemChannels.platform_views.invokeMethod<void>('resize', <String, dynamic> {
'id': id, 'id': id,
'width': size.width, 'width': size.width,
'height': size.height, 'height': size.height,
...@@ -526,7 +526,7 @@ class AndroidViewController { ...@@ -526,7 +526,7 @@ class AndroidViewController {
if (_state == _AndroidViewState.waitingForSize) if (_state == _AndroidViewState.waitingForSize)
return; return;
await SystemChannels.platform_views.invokeMethod('setDirection', <String, dynamic> { await SystemChannels.platform_views.invokeMethod<void>('setDirection', <String, dynamic> {
'id': id, 'id': id,
'direction': _getAndroidDirection(layoutDirection), 'direction': _getAndroidDirection(layoutDirection),
}); });
...@@ -550,7 +550,7 @@ class AndroidViewController { ...@@ -550,7 +550,7 @@ class AndroidViewController {
/// See documentation of [MotionEvent.obtain](https://developer.android.com/reference/android/view/MotionEvent.html#obtain(long,%20long,%20int,%20float,%20float,%20float,%20float,%20int,%20float,%20float,%20int,%20int)) /// See documentation of [MotionEvent.obtain](https://developer.android.com/reference/android/view/MotionEvent.html#obtain(long,%20long,%20int,%20float,%20float,%20float,%20float,%20int,%20float,%20float,%20int,%20int))
/// for description of the parameters. /// for description of the parameters.
Future<void> sendMotionEvent(AndroidMotionEvent event) async { Future<void> sendMotionEvent(AndroidMotionEvent event) async {
await SystemChannels.platform_views.invokeMethod( await SystemChannels.platform_views.invokeMethod<dynamic>(
'touch', 'touch',
event._asList(id), event._asList(id),
); );
...@@ -649,6 +649,6 @@ class UiKitViewController { ...@@ -649,6 +649,6 @@ class UiKitViewController {
/// disposed. /// disposed.
Future<void> dispose() async { Future<void> dispose() async {
_debugDisposed = true; _debugDisposed = true;
await SystemChannels.platform_views.invokeMethod('dispose', id); await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
} }
} }
...@@ -238,7 +238,7 @@ class SystemChrome { ...@@ -238,7 +238,7 @@ class SystemChrome {
/// The empty list causes the application to defer to the operating system /// The empty list causes the application to defer to the operating system
/// default. /// default.
static Future<void> setPreferredOrientations(List<DeviceOrientation> orientations) async { static Future<void> setPreferredOrientations(List<DeviceOrientation> orientations) async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'SystemChrome.setPreferredOrientations', 'SystemChrome.setPreferredOrientations',
_stringify(orientations), _stringify(orientations),
); );
...@@ -250,7 +250,7 @@ class SystemChrome { ...@@ -250,7 +250,7 @@ class SystemChrome {
/// Any part of the description that is unsupported on the current platform /// Any part of the description that is unsupported on the current platform
/// will be ignored. /// will be ignored.
static Future<void> setApplicationSwitcherDescription(ApplicationSwitcherDescription description) async { static Future<void> setApplicationSwitcherDescription(ApplicationSwitcherDescription description) async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'SystemChrome.setApplicationSwitcherDescription', 'SystemChrome.setApplicationSwitcherDescription',
<String, dynamic>{ <String, dynamic>{
'label': description.label, 'label': description.label,
...@@ -282,7 +282,7 @@ class SystemChrome { ...@@ -282,7 +282,7 @@ class SystemChrome {
/// or calling this again. Otherwise, the original UI overlay settings will be /// or calling this again. Otherwise, the original UI overlay settings will be
/// automatically restored only when the application loses and regains focus. /// automatically restored only when the application loses and regains focus.
static Future<void> setEnabledSystemUIOverlays(List<SystemUiOverlay> overlays) async { static Future<void> setEnabledSystemUIOverlays(List<SystemUiOverlay> overlays) async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'SystemChrome.setEnabledSystemUIOverlays', 'SystemChrome.setEnabledSystemUIOverlays',
_stringify(overlays), _stringify(overlays),
); );
...@@ -298,7 +298,7 @@ class SystemChrome { ...@@ -298,7 +298,7 @@ class SystemChrome {
/// On Android, the system UI cannot be changed until 1 second after the previous /// On Android, the system UI cannot be changed until 1 second after the previous
/// change. This is to prevent malware from permanently hiding navigation buttons. /// change. This is to prevent malware from permanently hiding navigation buttons.
static Future<void> restoreSystemUIOverlays() async { static Future<void> restoreSystemUIOverlays() async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'SystemChrome.restoreSystemUIOverlays', 'SystemChrome.restoreSystemUIOverlays',
null, null,
); );
...@@ -349,7 +349,7 @@ class SystemChrome { ...@@ -349,7 +349,7 @@ class SystemChrome {
scheduleMicrotask(() { scheduleMicrotask(() {
assert(_pendingStyle != null); assert(_pendingStyle != null);
if (_pendingStyle != _latestStyle) { if (_pendingStyle != _latestStyle) {
SystemChannels.platform.invokeMethod( SystemChannels.platform.invokeMethod<void>(
'SystemChrome.setSystemUIOverlayStyle', 'SystemChrome.setSystemUIOverlayStyle',
_pendingStyle._toMap(), _pendingStyle._toMap(),
); );
......
...@@ -20,6 +20,6 @@ class SystemNavigator { ...@@ -20,6 +20,6 @@ class SystemNavigator {
/// the latter may cause the underlying platform to act as if the application /// the latter may cause the underlying platform to act as if the application
/// had crashed. /// had crashed.
static Future<void> pop() async { static Future<void> pop() async {
await SystemChannels.platform.invokeMethod('SystemNavigator.pop'); await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop');
} }
} }
...@@ -20,7 +20,7 @@ class SystemSound { ...@@ -20,7 +20,7 @@ class SystemSound {
/// Play the specified system sound. If that sound is not present on the /// Play the specified system sound. If that sound is not present on the
/// system, the call is ignored. /// system, the call is ignored.
static Future<void> play(SystemSoundType type) async { static Future<void> play(SystemSoundType type) async {
await SystemChannels.platform.invokeMethod( await SystemChannels.platform.invokeMethod<void>(
'SystemSound.play', 'SystemSound.play',
type.toString(), type.toString(),
); );
......
...@@ -626,13 +626,13 @@ class TextInputConnection { ...@@ -626,13 +626,13 @@ class TextInputConnection {
/// Requests that the text input control become visible. /// Requests that the text input control become visible.
void show() { void show() {
assert(attached); assert(attached);
SystemChannels.textInput.invokeMethod('TextInput.show'); SystemChannels.textInput.invokeMethod<void>('TextInput.show');
} }
/// Requests that the text input control change its internal state to match the given state. /// Requests that the text input control change its internal state to match the given state.
void setEditingState(TextEditingValue value) { void setEditingState(TextEditingValue value) {
assert(attached); assert(attached);
SystemChannels.textInput.invokeMethod( SystemChannels.textInput.invokeMethod<void>(
'TextInput.setEditingState', 'TextInput.setEditingState',
value.toJSON(), value.toJSON(),
); );
...@@ -644,7 +644,7 @@ class TextInputConnection { ...@@ -644,7 +644,7 @@ class TextInputConnection {
/// other client attaches to it within this animation frame. /// other client attaches to it within this animation frame.
void close() { void close() {
if (attached) { if (attached) {
SystemChannels.textInput.invokeMethod('TextInput.clearClient'); SystemChannels.textInput.invokeMethod<void>('TextInput.clearClient');
_clientHandler _clientHandler
.._currentConnection = null .._currentConnection = null
.._scheduleHide(); .._scheduleHide();
...@@ -749,7 +749,7 @@ class _TextInputClientHandler { ...@@ -749,7 +749,7 @@ class _TextInputClientHandler {
scheduleMicrotask(() { scheduleMicrotask(() {
_hidePending = false; _hidePending = false;
if (_currentConnection == null) if (_currentConnection == null)
SystemChannels.textInput.invokeMethod('TextInput.hide'); SystemChannels.textInput.invokeMethod<void>('TextInput.hide');
}); });
} }
} }
...@@ -802,7 +802,7 @@ class TextInput { ...@@ -802,7 +802,7 @@ class TextInput {
assert(_debugEnsureInputActionWorksOnPlatform(configuration.inputAction)); assert(_debugEnsureInputActionWorksOnPlatform(configuration.inputAction));
final TextInputConnection connection = TextInputConnection._(client); final TextInputConnection connection = TextInputConnection._(client);
_clientHandler._currentConnection = connection; _clientHandler._currentConnection = connection;
SystemChannels.textInput.invokeMethod( SystemChannels.textInput.invokeMethod<void>(
'TextInput.setClient', 'TextInput.setClient',
<dynamic>[ connection._id, configuration.toJson() ], <dynamic>[ connection._id, configuration.toJson() ],
); );
......
...@@ -43,15 +43,49 @@ void main() { ...@@ -43,15 +43,49 @@ void main() {
'ch7', 'ch7',
(ByteData message) async { (ByteData message) async {
final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message); final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message);
if (methodCall['method'] == 'sayHello') if (methodCall['method'] == 'sayHello') {
return jsonMessage.encodeMessage(<dynamic>['${methodCall['args']} world']); return jsonMessage.encodeMessage(<dynamic>['${methodCall['args']} world']);
else } else {
return jsonMessage.encodeMessage(<dynamic>['unknown', null, null]); return jsonMessage.encodeMessage(<dynamic>['unknown', null, null]);
}
}, },
); );
final String result = await channel.invokeMethod('sayHello', 'hello'); final String result = await channel.invokeMethod('sayHello', 'hello');
expect(result, equals('hello world')); expect(result, equals('hello world'));
}); });
test('can invoke list method and get result', () async {
BinaryMessages.setMockMessageHandler(
'ch7',
(ByteData message) async {
final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message);
if (methodCall['method'] == 'sayHello') {
return jsonMessage.encodeMessage(<dynamic>[<String>['${methodCall['args']}', 'world']]);
} else {
return jsonMessage.encodeMessage(<dynamic>['unknown', null, null]);
}
},
);
expect(channel.invokeMethod<List<String>>('sayHello', 'hello'), throwsA(isInstanceOf<TypeError>()));
expect(await channel.invokeListMethod<String>('sayHello', 'hello'), <String>['hello', 'world']);
});
test('can invoke map method and get result', () async {
BinaryMessages.setMockMessageHandler(
'ch7',
(ByteData message) async {
final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message);
if (methodCall['method'] == 'sayHello') {
return jsonMessage.encodeMessage(<dynamic>[<String, String>{'${methodCall['args']}': 'world'}]);
} else {
return jsonMessage.encodeMessage(<dynamic>['unknown', null, null]);
}
},
);
expect(channel.invokeMethod<Map<String, String>>('sayHello', 'hello'), throwsA(isInstanceOf<TypeError>()));
expect(await channel.invokeMapMethod<String, String>('sayHello', 'hello'), <String, String>{'hello': 'world'});
});
test('can invoke method and get error', () async { test('can invoke method and get error', () async {
BinaryMessages.setMockMessageHandler( BinaryMessages.setMockMessageHandler(
'ch7', 'ch7',
...@@ -64,7 +98,7 @@ void main() { ...@@ -64,7 +98,7 @@ void main() {
}, },
); );
try { try {
await channel.invokeMethod('sayHello', 'hello'); await channel.invokeMethod<dynamic>('sayHello', 'hello');
fail('Exception expected'); fail('Exception expected');
} on PlatformException catch (e) { } on PlatformException catch (e) {
expect(e.code, equals('bad')); expect(e.code, equals('bad'));
...@@ -80,7 +114,7 @@ void main() { ...@@ -80,7 +114,7 @@ void main() {
(ByteData message) async => null, (ByteData message) async => null,
); );
try { try {
await channel.invokeMethod('sayHello', 'hello'); await channel.invokeMethod<void>('sayHello', 'hello');
fail('Exception expected'); fail('Exception expected');
} on MissingPluginException catch (e) { } on MissingPluginException catch (e) {
expect(e.message, contains('sayHello')); expect(e.message, contains('sayHello'));
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment