【Flutter/Dart】MethodChannelでネイティブコード(Swift/Kotlin)を使用する方法
この記事からわかること
- Flutter/Dartでネイティブコードを使用する方法
- MethodChannelの使い方
index
[open]
\ アプリをリリースしました /
- Android Studio:Narwhal Feature Drop
- Android OS:15以降
- Kotlin:2.2.21
- Xcode:26.0.1
- iOS:26
- Swift:6
- Flutter:3.38.3
- Dart:3.10.1
- macOS(M1):Tahoe 26.0.1
MethodChannel:ネイティブコードをDartから呼び出す方法
Flutterで開発しているプロジェクトの中でネイティブコードを呼び出す方法として「MethodChannel」が用意されています。対応しているのは以下のプラットフォームの固有言語になります。
- Android : Kotlin、Java
- iOS : Swift、Objective-C
- Windows : C++
- macOS : Objective-C
- Linux : C
FlutterではDartとネイティブコードは直接やりとりができるわけではなくこの「MethodChannel」が橋渡し役になります。やり取りを行うために特定のチャネルを介してデータを送受信します。
+----------------------+ +-----------------------+
| Flutter(Dart) | | Native(iOS/Android) |
| | | |
| invokeMethod() | =======> | MethodCallHandler() |
| | | |
| result() | <======= | return |
+----------------------+ +-----------------------+
実装方法
Dart側ではinvokeMethodメソッドを使用して「MethodChannel」を経由して処理を呼び出します。ネイティブ側ではMethodCallHandlerを使用してDart側からの呼び出しに対して処理を実行し結果を返します。ざっくりと実装の流れは以下の通りになります。
- Dart側:MethodChannelをインスタンス化
- Dart側:invokeMethodをメソッドラベルを指定して呼び出す
- ネイティブ側:MethodChannelをインスタンス化
- ネイティブ側:setMethodCallHandlerで処理を呼び出し結果を返す
今回はサンプルとしてネイティブコードを使用してバッテリー情報を取得してDart側のUIで表示する実装をしていきたいと思います。全体のコードを最後に記載しておきます。
1.Dart側:MethodChannelをインスタンス化
まずはDart側での呼び出し処理から記述していきます。MethodChannelの引数にチャンネル名を指定してインスタンス化します。このチャンネル名はネイティブ側でも指定するものになります。
class _BatteryPageState extends State<BatteryPage> {
static const methodChannel = MethodChannel('samples.flutter.dev/battery');
// 〜〜〜〜〜
}
2.Dart側:invokeMethodをメソッドラベルを指定して呼び出す
MethodChannel型の持つinvokeMethodを使用してネイティブのコードの実行結果を取得します。引数にはメソッドラベルを指定します。このメソッドラベルもネイティブ側でも指定するものになります。またinvokeMethodでは返り値はFuture型になります。非同期処理になるのでawaitを付与する必要とエラーをスローする可能性があるので注意してください。
Future<void> _getBatteryLevel() async {
try {
// ネイティブコードの処理を呼び出して実行する
final int level =
await methodChannel.invokeMethod<int>('getBatteryLevel') ?? -1;
setState(() {
batteryLevel = 'Battery level: $level%';
});
} on PlatformException catch (e) {
setState(() {
batteryLevel = "Error: ${e.message}";
});
}
}
3.ネイティブ側:MethodChannelをインスタンス化(Swift)
まずはiOS側から見ていきます。MethodChannelを使用するにはbinaryMessengerが取得できるところとFlutterEngineに紐づいている箇所でしか使用できないので注意してください。SwiftではFlutterMethodChannelでチャンネルをインスタンス化します。
@main
@objc class AppDelegate: FlutterAppDelegate {
private let channelName = "samples.flutter.dev/battery"
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
// ここから
let controller = window?.rootViewController as! FlutterViewController
let batteryChannel = FlutterMethodChannel(
name: channelName,
binaryMessenger: controller.binaryMessenger
)
// TODO: setMethodCallHandlerの処理を実装
// ここまで追加
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
4.ネイティブ側:setMethodCallHandlerで処理を呼び出し結果を返す(Swift)
Dart側に処理結果を送信するためにsetMethodCallHandlerを使用します。コールバックになっており、その中で呼び出し時に指定されたメソッドラベルが取得できます。そして結果をresult()で返してあげればOKです。意図的にFlutterErrorを返すことも可能です。
batteryChannel.setMethodCallHandler { (call, result) in
if call.method == "getBatteryLevel" {
let level = self.getBatteryLevel()
if true {
result(level)
} else {
result(FlutterError(code: "UNAVAILABLE", message: "Battery IOS info unavailable", details: nil))
}
} else {
result(FlutterMethodNotImplemented)
}
}
バッテリー取得処理は以下のように実装しておきます。詳細はこちらの記事を参考にしてください。
private func getBatteryLevel() -> Int {
UIDevice.current.isBatteryMonitoringEnabled = true
let batteryLevel = UIDevice.current.batteryLevel
return Int(batteryLevel * 100)
}
3.ネイティブ側:MethodChannelをインスタンス化(Kotlin)
続いてAndroid側の実装です。こちらはMethodChannelでインスタンス化することができます。
class MainActivity : FlutterActivity() {
private val CHANNEL = "samples.flutter.dev/battery"
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL
)
}
}
4.ネイティブ側:setMethodCallHandlerで処理を呼び出し結果を返す(Kotlin)
Swiftと同じような形式でsetMethodCallHandlerが用意されているので結果を返してあげればOKです。
MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL
).setMethodCallHandler { call, result ->
when (call.method) {
"getBatteryLevel" -> {
val level = getBatteryLevel()
if (level != -1) {
result.success(level)
} else {
result.error("UNAVAILABLE", "Battery level not available.", null)
}
}
else -> result.notImplemented()
}
}
バッテリー取得処理は以下のように実装しておきます。
private fun getBatteryLevel(): Int {
val batteryManager = getSystemService(Context.BATTERY_SERVICE) as BatteryManager
return batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY)
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。







