mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Adds a type parameter to invokeMethod (and additional utility methods) (#26303)
This commit is contained in:
parent
dca8d36d62
commit
83af6f48d6
@ -26,7 +26,7 @@ Future<String> dataHandler(String message) async {
|
||||
final Completer<String> completer = Completer<String>();
|
||||
final int id = int.tryParse(message.split('#')[1]) ?? 0;
|
||||
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,
|
||||
});
|
||||
completer.complete(json.encode(result));
|
||||
|
@ -125,15 +125,15 @@ class PlatformViewState extends State<PlatformViewPage> {
|
||||
.cast<Map<dynamic, dynamic>>()
|
||||
.map<Map<String, dynamic>>((Map<dynamic, dynamic> e) =>e.cast<String, dynamic>())
|
||||
.toList();
|
||||
await channel.invokeMethod('pipeFlutterViewEvents');
|
||||
await viewChannel.invokeMethod('pipeTouchEvents');
|
||||
await channel.invokeMethod<void>('pipeFlutterViewEvents');
|
||||
await viewChannel.invokeMethod<void>('pipeTouchEvents');
|
||||
print('replaying ${recordedEvents.length} motion events');
|
||||
for (Map<String, dynamic> event in recordedEvents.reversed) {
|
||||
await channel.invokeMethod('synthesizeEvent', event);
|
||||
await channel.invokeMethod<void>('synthesizeEvent', event);
|
||||
}
|
||||
|
||||
await channel.invokeMethod('stopFlutterViewEvents');
|
||||
await viewChannel.invokeMethod('stopTouchEvents');
|
||||
await channel.invokeMethod<void>('stopFlutterViewEvents');
|
||||
await viewChannel.invokeMethod<void>('stopTouchEvents');
|
||||
|
||||
if (flutterViewEvents.length != embeddedViewEvents.length)
|
||||
return 'Synthesized ${flutterViewEvents.length} events but the embedded view received ${embeddedViewEvents.length} events';
|
||||
@ -160,7 +160,7 @@ class PlatformViewState extends State<PlatformViewPage> {
|
||||
}
|
||||
|
||||
Future<void> saveRecordedEvents(ByteData data, BuildContext context) async {
|
||||
if (!await channel.invokeMethod('getStoragePermission')) {
|
||||
if (!await channel.invokeMethod<bool>('getStoragePermission')) {
|
||||
showMessage(
|
||||
context, 'External storage permissions are required to save events');
|
||||
return;
|
||||
@ -190,11 +190,11 @@ class PlatformViewState extends State<PlatformViewPage> {
|
||||
}
|
||||
|
||||
void listenToFlutterViewEvents() {
|
||||
channel.invokeMethod('pipeFlutterViewEvents');
|
||||
viewChannel.invokeMethod('pipeTouchEvents');
|
||||
channel.invokeMethod<void>('pipeFlutterViewEvents');
|
||||
viewChannel.invokeMethod<void>('pipeTouchEvents');
|
||||
Timer(const Duration(seconds: 3), () {
|
||||
channel.invokeMethod('stopFlutterViewEvents');
|
||||
viewChannel.invokeMethod('stopTouchEvents');
|
||||
channel.invokeMethod<void>('stopFlutterViewEvents');
|
||||
viewChannel.invokeMethod<void>('stopTouchEvents');
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ Future<TestStepResult> _methodCallSuccessHandshake(
|
||||
dynamic result = nothing;
|
||||
dynamic error = nothing;
|
||||
try {
|
||||
result = await channel.invokeMethod('success', arguments);
|
||||
result = await channel.invokeMethod<dynamic>('success', arguments);
|
||||
} catch (e) {
|
||||
error = e;
|
||||
}
|
||||
@ -95,7 +95,7 @@ Future<TestStepResult> _methodCallErrorHandshake(
|
||||
dynamic errorDetails = nothing;
|
||||
dynamic error = nothing;
|
||||
try {
|
||||
error = await channel.invokeMethod('error', arguments);
|
||||
error = await channel.invokeMethod<dynamic>('error', arguments);
|
||||
} on PlatformException catch (e) {
|
||||
errorDetails = e.details;
|
||||
} catch (e) {
|
||||
@ -123,7 +123,7 @@ Future<TestStepResult> _methodCallNotImplementedHandshake(
|
||||
dynamic result = nothing;
|
||||
dynamic error = nothing;
|
||||
try {
|
||||
error = await channel.invokeMethod('notImplemented');
|
||||
error = await channel.invokeMethod<dynamic>('notImplemented');
|
||||
} on MissingPluginException {
|
||||
result = null;
|
||||
} catch (e) {
|
||||
|
@ -47,11 +47,11 @@ Widget builds: $_widgetBuilds''';
|
||||
_summary = 'Producing texture frames at .5x speed...';
|
||||
_state = FrameState.slow;
|
||||
_icon = Icons.stop;
|
||||
channel.invokeMethod('start', _flutterFrameRate ~/ 2);
|
||||
channel.invokeMethod<void>('start', _flutterFrameRate ~/ 2);
|
||||
break;
|
||||
case FrameState.slow:
|
||||
debugPrint('Stopping .5x speed test...');
|
||||
await channel.invokeMethod('stop');
|
||||
await channel.invokeMethod<void>('stop');
|
||||
await _summarizeStats();
|
||||
_icon = Icons.fast_forward;
|
||||
_state = FrameState.afterSlow;
|
||||
@ -62,11 +62,11 @@ Widget builds: $_widgetBuilds''';
|
||||
_summary = 'Producing texture frames at 2x speed...';
|
||||
_state = FrameState.fast;
|
||||
_icon = Icons.stop;
|
||||
channel.invokeMethod('start', (_flutterFrameRate * 2).toInt());
|
||||
channel.invokeMethod<void>('start', (_flutterFrameRate * 2).toInt());
|
||||
break;
|
||||
case FrameState.fast:
|
||||
debugPrint('Stopping 2x speed test...');
|
||||
await channel.invokeMethod('stop');
|
||||
await channel.invokeMethod<void>('stop');
|
||||
await _summarizeStats();
|
||||
_state = FrameState.afterFast;
|
||||
_icon = Icons.replay;
|
||||
|
@ -18,7 +18,7 @@ class _FlavorState extends State<Flavor> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
const MethodChannel('flavor').invokeMethod('getFlavor').then((Object flavor) {
|
||||
const MethodChannel('flavor').invokeMethod<String>('getFlavor').then((String flavor) {
|
||||
setState(() {
|
||||
_flavor = flavor;
|
||||
});
|
||||
|
@ -82,10 +82,10 @@ Future<void> main() async {
|
||||
await controller.tap(find.byTooltip('Back'));
|
||||
}
|
||||
print('Finished successfully!');
|
||||
_kTestChannel.invokeMethod('success');
|
||||
_kTestChannel.invokeMethod<void>('success');
|
||||
} catch (error, stack) {
|
||||
print('Caught error: $error\n$stack');
|
||||
_kTestChannel.invokeMethod('failure');
|
||||
_kTestChannel.invokeMethod<void>('failure');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ class Clipboard {
|
||||
|
||||
/// Stores the given clipboard data on the clipboard.
|
||||
static Future<void> setData(ClipboardData data) async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'Clipboard.setData',
|
||||
<String, dynamic>{
|
||||
'text': data.text,
|
||||
|
@ -21,7 +21,7 @@ class HapticFeedback {
|
||||
/// On Android, this uses the platform haptic feedback API to simulate a
|
||||
/// response to a long press (`HapticFeedbackConstants.LONG_PRESS`).
|
||||
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.
|
||||
@ -32,7 +32,7 @@ class HapticFeedback {
|
||||
///
|
||||
/// On Android, this uses `HapticFeedbackConstants.VIRTUAL_KEY`.
|
||||
static Future<void> lightImpact() async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'HapticFeedback.vibrate',
|
||||
'HapticFeedbackType.lightImpact',
|
||||
);
|
||||
@ -46,7 +46,7 @@ class HapticFeedback {
|
||||
///
|
||||
/// On Android, this uses `HapticFeedbackConstants.KEYBOARD_TAP`.
|
||||
static Future<void> mediumImpact() async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'HapticFeedback.vibrate',
|
||||
'HapticFeedbackType.mediumImpact',
|
||||
);
|
||||
@ -61,7 +61,7 @@ class HapticFeedback {
|
||||
/// On Android, this uses `HapticFeedbackConstants.CONTEXT_CLICK` on API levels
|
||||
/// 23 and above. This call has no effects on Android API levels below 23.
|
||||
static Future<void> heavyImpact() async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'HapticFeedback.vibrate',
|
||||
'HapticFeedbackType.heavyImpact',
|
||||
);
|
||||
@ -74,7 +74,7 @@ class HapticFeedback {
|
||||
///
|
||||
/// On Android, this uses `HapticFeedbackConstants.CLOCK_TICK`.
|
||||
static Future<void> selectionClick() async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'HapticFeedback.vibrate',
|
||||
'HapticFeedbackType.selectionClick',
|
||||
);
|
||||
|
@ -128,6 +128,12 @@ class MethodChannel {
|
||||
/// result. The values supported by the default codec and their platform-specific
|
||||
/// 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:
|
||||
///
|
||||
/// * a result (possibly null), on successful invocation;
|
||||
@ -149,10 +155,9 @@ class MethodChannel {
|
||||
/// static const MethodChannel _channel = MethodChannel('music');
|
||||
///
|
||||
/// static Future<bool> isLicensed() async {
|
||||
/// // invokeMethod returns a Future<dynamic>, and we cannot pass that for
|
||||
/// // a Future<bool>, hence the indirection.
|
||||
/// final bool result = await _channel.invokeMethod('isLicensed');
|
||||
/// return result;
|
||||
/// // invokeMethod returns a Future<T> which can be inferred as bool
|
||||
/// // in this context.
|
||||
/// return _channel.invokeMethod('isLicensed');
|
||||
/// }
|
||||
///
|
||||
/// static Future<List<Song>> songs() async {
|
||||
@ -160,6 +165,7 @@ class MethodChannel {
|
||||
/// // List<dynamic> with Map<dynamic, dynamic> entries. Post-processing
|
||||
/// // code thus cannot assume e.g. List<Map<String, String>> even though
|
||||
/// // 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');
|
||||
/// return songs.map(Song.fromJson).toList();
|
||||
/// }
|
||||
@ -168,7 +174,7 @@ class MethodChannel {
|
||||
/// // Errors occurring on the platform side cause invokeMethod to throw
|
||||
/// // PlatformExceptions.
|
||||
/// try {
|
||||
/// await _channel.invokeMethod('play', <String, dynamic>{
|
||||
/// return _channel.invokeMethod('play', <String, dynamic>{
|
||||
/// 'song': song.id,
|
||||
/// 'volume': volume,
|
||||
/// });
|
||||
@ -275,21 +281,54 @@ class MethodChannel {
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [invokeListMethod], for automatically returning typed lists.
|
||||
/// * [invokeMapMethod], for automatically returning typed maps.
|
||||
/// * [StandardMessageCodec] which defines the payload values supported by
|
||||
/// [StandardMethodCodec].
|
||||
/// * [JSONMessageCodec] which defines the payload values supported by
|
||||
/// [JSONMethodCodec].
|
||||
/// * <https://docs.flutter.io/javadoc/io/flutter/plugin/common/MethodCall.html>
|
||||
/// 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);
|
||||
final dynamic result = await BinaryMessages.send(
|
||||
final ByteData result = await BinaryMessages.send(
|
||||
name,
|
||||
codec.encodeMethodCall(MethodCall(method, arguments)),
|
||||
);
|
||||
if (result == null)
|
||||
if (result == null) {
|
||||
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.
|
||||
@ -366,13 +405,27 @@ class OptionalMethodChannel extends MethodChannel {
|
||||
: super(name, codec);
|
||||
|
||||
@override
|
||||
Future<dynamic> invokeMethod(String method, [dynamic arguments]) async {
|
||||
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async {
|
||||
try {
|
||||
return await super.invokeMethod(method, arguments);
|
||||
final T result = await super.invokeMethod<T>(method, arguments);
|
||||
return result;
|
||||
} on MissingPluginException {
|
||||
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.
|
||||
@ -434,7 +487,7 @@ class EventChannel {
|
||||
return null;
|
||||
});
|
||||
try {
|
||||
await methodChannel.invokeMethod('listen', arguments);
|
||||
await methodChannel.invokeMethod<void>('listen', arguments);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
@ -446,7 +499,7 @@ class EventChannel {
|
||||
}, onCancel: () async {
|
||||
BinaryMessages.setMessageHandler(name, null);
|
||||
try {
|
||||
await methodChannel.invokeMethod('cancel', arguments);
|
||||
await methodChannel.invokeMethod<void>('cancel', arguments);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
|
@ -129,7 +129,7 @@ class PlatformViewsService {
|
||||
paramsByteData.lengthInBytes,
|
||||
);
|
||||
}
|
||||
await SystemChannels.platform_views.invokeMethod('create', args);
|
||||
await SystemChannels.platform_views.invokeMethod<void>('create', args);
|
||||
return UiKitViewController._(id, layoutDirection);
|
||||
}
|
||||
}
|
||||
@ -485,7 +485,7 @@ class AndroidViewController {
|
||||
/// disposed.
|
||||
Future<void> dispose() async {
|
||||
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;
|
||||
}
|
||||
|
||||
@ -504,7 +504,7 @@ class AndroidViewController {
|
||||
if (_state == _AndroidViewState.waitingForSize)
|
||||
return _create(size);
|
||||
|
||||
await SystemChannels.platform_views.invokeMethod('resize', <String, dynamic> {
|
||||
await SystemChannels.platform_views.invokeMethod<void>('resize', <String, dynamic> {
|
||||
'id': id,
|
||||
'width': size.width,
|
||||
'height': size.height,
|
||||
@ -526,7 +526,7 @@ class AndroidViewController {
|
||||
if (_state == _AndroidViewState.waitingForSize)
|
||||
return;
|
||||
|
||||
await SystemChannels.platform_views.invokeMethod('setDirection', <String, dynamic> {
|
||||
await SystemChannels.platform_views.invokeMethod<void>('setDirection', <String, dynamic> {
|
||||
'id': id,
|
||||
'direction': _getAndroidDirection(layoutDirection),
|
||||
});
|
||||
@ -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))
|
||||
/// for description of the parameters.
|
||||
Future<void> sendMotionEvent(AndroidMotionEvent event) async {
|
||||
await SystemChannels.platform_views.invokeMethod(
|
||||
await SystemChannels.platform_views.invokeMethod<dynamic>(
|
||||
'touch',
|
||||
event._asList(id),
|
||||
);
|
||||
@ -649,6 +649,6 @@ class UiKitViewController {
|
||||
/// disposed.
|
||||
Future<void> dispose() async {
|
||||
_debugDisposed = true;
|
||||
await SystemChannels.platform_views.invokeMethod('dispose', id);
|
||||
await SystemChannels.platform_views.invokeMethod<void>('dispose', id);
|
||||
}
|
||||
}
|
||||
|
@ -238,7 +238,7 @@ class SystemChrome {
|
||||
/// The empty list causes the application to defer to the operating system
|
||||
/// default.
|
||||
static Future<void> setPreferredOrientations(List<DeviceOrientation> orientations) async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'SystemChrome.setPreferredOrientations',
|
||||
_stringify(orientations),
|
||||
);
|
||||
@ -250,7 +250,7 @@ class SystemChrome {
|
||||
/// Any part of the description that is unsupported on the current platform
|
||||
/// will be ignored.
|
||||
static Future<void> setApplicationSwitcherDescription(ApplicationSwitcherDescription description) async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'SystemChrome.setApplicationSwitcherDescription',
|
||||
<String, dynamic>{
|
||||
'label': description.label,
|
||||
@ -282,7 +282,7 @@ class SystemChrome {
|
||||
/// or calling this again. Otherwise, the original UI overlay settings will be
|
||||
/// automatically restored only when the application loses and regains focus.
|
||||
static Future<void> setEnabledSystemUIOverlays(List<SystemUiOverlay> overlays) async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'SystemChrome.setEnabledSystemUIOverlays',
|
||||
_stringify(overlays),
|
||||
);
|
||||
@ -298,7 +298,7 @@ class SystemChrome {
|
||||
/// 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.
|
||||
static Future<void> restoreSystemUIOverlays() async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'SystemChrome.restoreSystemUIOverlays',
|
||||
null,
|
||||
);
|
||||
@ -349,7 +349,7 @@ class SystemChrome {
|
||||
scheduleMicrotask(() {
|
||||
assert(_pendingStyle != null);
|
||||
if (_pendingStyle != _latestStyle) {
|
||||
SystemChannels.platform.invokeMethod(
|
||||
SystemChannels.platform.invokeMethod<void>(
|
||||
'SystemChrome.setSystemUIOverlayStyle',
|
||||
_pendingStyle._toMap(),
|
||||
);
|
||||
|
@ -20,6 +20,6 @@ class SystemNavigator {
|
||||
/// the latter may cause the underlying platform to act as if the application
|
||||
/// had crashed.
|
||||
static Future<void> pop() async {
|
||||
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
|
||||
await SystemChannels.platform.invokeMethod<void>('SystemNavigator.pop');
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ class SystemSound {
|
||||
/// Play the specified system sound. If that sound is not present on the
|
||||
/// system, the call is ignored.
|
||||
static Future<void> play(SystemSoundType type) async {
|
||||
await SystemChannels.platform.invokeMethod(
|
||||
await SystemChannels.platform.invokeMethod<void>(
|
||||
'SystemSound.play',
|
||||
type.toString(),
|
||||
);
|
||||
|
@ -626,13 +626,13 @@ class TextInputConnection {
|
||||
/// Requests that the text input control become visible.
|
||||
void show() {
|
||||
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.
|
||||
void setEditingState(TextEditingValue value) {
|
||||
assert(attached);
|
||||
SystemChannels.textInput.invokeMethod(
|
||||
SystemChannels.textInput.invokeMethod<void>(
|
||||
'TextInput.setEditingState',
|
||||
value.toJSON(),
|
||||
);
|
||||
@ -644,7 +644,7 @@ class TextInputConnection {
|
||||
/// other client attaches to it within this animation frame.
|
||||
void close() {
|
||||
if (attached) {
|
||||
SystemChannels.textInput.invokeMethod('TextInput.clearClient');
|
||||
SystemChannels.textInput.invokeMethod<void>('TextInput.clearClient');
|
||||
_clientHandler
|
||||
.._currentConnection = null
|
||||
.._scheduleHide();
|
||||
@ -749,7 +749,7 @@ class _TextInputClientHandler {
|
||||
scheduleMicrotask(() {
|
||||
_hidePending = false;
|
||||
if (_currentConnection == null)
|
||||
SystemChannels.textInput.invokeMethod('TextInput.hide');
|
||||
SystemChannels.textInput.invokeMethod<void>('TextInput.hide');
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -802,7 +802,7 @@ class TextInput {
|
||||
assert(_debugEnsureInputActionWorksOnPlatform(configuration.inputAction));
|
||||
final TextInputConnection connection = TextInputConnection._(client);
|
||||
_clientHandler._currentConnection = connection;
|
||||
SystemChannels.textInput.invokeMethod(
|
||||
SystemChannels.textInput.invokeMethod<void>(
|
||||
'TextInput.setClient',
|
||||
<dynamic>[ connection._id, configuration.toJson() ],
|
||||
);
|
||||
|
@ -43,15 +43,49 @@ void main() {
|
||||
'ch7',
|
||||
(ByteData message) async {
|
||||
final Map<dynamic, dynamic> methodCall = jsonMessage.decodeMessage(message);
|
||||
if (methodCall['method'] == 'sayHello')
|
||||
if (methodCall['method'] == 'sayHello') {
|
||||
return jsonMessage.encodeMessage(<dynamic>['${methodCall['args']} world']);
|
||||
else
|
||||
} else {
|
||||
return jsonMessage.encodeMessage(<dynamic>['unknown', null, null]);
|
||||
}
|
||||
},
|
||||
);
|
||||
final String result = await channel.invokeMethod('sayHello', 'hello');
|
||||
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 {
|
||||
BinaryMessages.setMockMessageHandler(
|
||||
'ch7',
|
||||
@ -64,7 +98,7 @@ void main() {
|
||||
},
|
||||
);
|
||||
try {
|
||||
await channel.invokeMethod('sayHello', 'hello');
|
||||
await channel.invokeMethod<dynamic>('sayHello', 'hello');
|
||||
fail('Exception expected');
|
||||
} on PlatformException catch (e) {
|
||||
expect(e.code, equals('bad'));
|
||||
@ -80,7 +114,7 @@ void main() {
|
||||
(ByteData message) async => null,
|
||||
);
|
||||
try {
|
||||
await channel.invokeMethod('sayHello', 'hello');
|
||||
await channel.invokeMethod<void>('sayHello', 'hello');
|
||||
fail('Exception expected');
|
||||
} on MissingPluginException catch (e) {
|
||||
expect(e.message, contains('sayHello'));
|
||||
|
Loading…
Reference in New Issue
Block a user