invokeMethod<T> method
Invokes a method
on this channel with the specified arguments
.
The static type of arguments
is dynamic
, but only values supported by
the codec of this channel can be used. The same applies to the returned
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;
- a PlatformException, if the invocation failed in the platform plugin;
- a MissingPluginException, if the method has not been implemented by a platform plugin.
The following code snippets demonstrate how to invoke platform methods in Dart using a MethodChannel and how to implement those methods in Java (for Android) and Objective-C (for iOS).
{@tool snippet}
The code might be packaged up as a musical plugin, see flutter.dev/to/develop-packages:
abstract final class Music {
static const MethodChannel _channel = MethodChannel('music');
static Future<bool> isLicensed() async {
// invokeMethod returns a Future<T?>, so we handle the case where
// the return value is null by treating null as false.
return _channel.invokeMethod<bool>('isLicensed').then<bool>((bool? value) => value ?? false);
}
static Future<List<Song>> songs() async {
// invokeMethod here returns a Future<dynamic> that completes to a
// 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<List<dynamic>>('getSongs');
return songs?.cast<Map<String, Object?>>().map<Song>(Song.fromJson).toList() ?? <Song>[];
}
static Future<void> play(Song song, double volume) async {
// Errors occurring on the platform side cause invokeMethod to throw
// PlatformExceptions.
try {
return _channel.invokeMethod('play', <String, dynamic>{
'song': song.id,
'volume': volume,
});
} on PlatformException catch (e) {
throw ArgumentError('Unable to play ${song.title}: ${e.message}');
}
}
}
class Song {
Song(this.id, this.title, this.artist);
final String id;
final String title;
final String artist;
static Song fromJson(Map<String, Object?> json) {
return Song(json['id']! as String, json['title']! as String, json['artist']! as String);
}
}
{@end-tool}
{@tool snippet}
Java (for Android):
// Assumes existence of an Android MusicApi.
public class MusicPlugin implements MethodCallHandler {
@Override
public void onMethodCall(MethodCall call, Result result) {
switch (call.method) {
case "isLicensed":
result.success(MusicApi.checkLicense());
break;
case "getSongs":
final List<MusicApi.Track> tracks = MusicApi.getTracks();
final List<Object> json = ArrayList<>(tracks.size());
for (MusicApi.Track track : tracks) {
json.add(track.toJson()); // Map<String, Object> entries
}
result.success(json);
break;
case "play":
final String song = call.argument("song");
final double volume = call.argument("volume");
try {
MusicApi.playSongAtVolume(song, volume);
result.success(null);
} catch (MusicalException e) {
result.error("playError", e.getMessage(), null);
}
break;
default:
result.notImplemented();
}
}
// Other methods elided.
}
{@end-tool}
{@tool snippet}
Objective-C (for iOS):
@interface MusicPlugin : NSObject<FlutterPlugin>
@end
// Assumes existence of an iOS Broadway Play Api.
@implementation MusicPlugin
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
if ([@"isLicensed" isEqualToString:call.method]) {
result([NSNumber numberWithBool:[BWPlayApi isLicensed]]);
} else if ([@"getSongs" isEqualToString:call.method]) {
NSArray* items = [BWPlayApi items];
NSMutableArray* json = [NSMutableArray arrayWithCapacity:items.count];
for (final BWPlayItem* item in items) {
[json addObject:@{ @"id":item.itemId, @"title":item.name, @"artist":item.artist }];
}
result(json);
} else if ([@"play" isEqualToString:call.method]) {
NSString* itemId = call.arguments[@"song"];
NSNumber* volume = call.arguments[@"volume"];
NSError* error = nil;
BOOL success = [BWPlayApi playItem:itemId volume:volume.doubleValue error:&error];
if (success) {
result(nil);
} else {
result([FlutterError errorWithCode:[NSString stringWithFormat:@"Error %ld", error.code]
message:error.domain
details:error.localizedDescription]);
}
} else {
result(FlutterMethodNotImplemented);
}
}
// Other methods elided.
@end
{@end-tool}
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.
- api.flutter.dev/javadoc/io/flutter/plugin/common/MethodCall.html for how to access method call arguments on Android.
Implementation
@optionalTypeArgs
Future<T> invokeMethod<T>(String method, [dynamic arguments]) {
switch (method) {
case 'hasPermission':
case 'requestPermission':
return Future<bool>.value(true) as Future<T>;
case 'getPhoneNumber':
return Future<String>.value('') as Future<T>;
case 'getSimCardList':
return Future<String>.value('[]') as Future<T>;
default:
throw 'Unhandled stub method';
}
}