スマレジの テックファーム(SES 部門) でWebエンジニアとして働いている やまて(@r_yamate) と申します。
実務では SES の派遣先で、テーブルオーダーシステムの機能改修業務の設計などを担当しています。(2022 年 3 月〜 2023 年 3 月末予定)
2023 年 1 月からは Flutter を実務で使用することになって、キャッチアップ中です。
はじめに
この記事では、 Flutter の MethodChannel を使って Kotlin のコードを実行するサンプルアプリの実装手順をまとめます。
MethodChannel とは
MethodChannel は、 Flutter とネイティブプラットフォーム(今回は Android)の間でメッセージをやり取りするための仕組みです。この機能を使うことで、 Flutter アプリがネイティブプラットフォームの機能を呼び出すことができます。
(実務で Flutter から Android アプリ用の SDK ライブラリを利用するために MethodChannel を使うことがあり、使い方を確認しました。)
実装するサンプルアプリ
サンプルアプリでは、ユーザーが a
と b
(例:10
と 20
)の2つの数値をが入力して「計算」ボタンを押すと、a+b
の計算結果(例:30
)を返す、という簡単な機能を実装します。
Flutter(Dart)で画面を作成し、 Dart は Kotlin に MethodChannel で接続して、 Kotlin で書いた a+b
の計算をするメソッドを呼び出します。
Kotlin で計算した結果を Flutter に返し、返した値をアプリ画面に表示します。
処理フローのイメージ
※ 「a+b の計算処理を実行」する処理の部分を、Android アプリ用の SDK ライブラリで API 実行する処理にすれば、その処理結果を Flutter 側に返すことができます。
※ MethodChannel は、一意の識別子(例: CHANNEL
定数)を使って Flutter 側とネイティブ側で通信を行います。 Flutter 側からメソッドを呼び出すと、ネイティブ側の MethodChannel で設定されたハンドラが呼び出され、メソッド名や引数を受け取って処理を行います。処理が終わったら、結果を Flutter 側に返すことができます。この仕組みを使って、 Flutter アプリとネイティブプラットフォームのコードを相互に呼び出して連携させることができます。
環境
目次
今回の記事の内容のコードです。
1. Flutter プロジェクトの作成
IDE や flutter create コマンドで、Flutter プロジェクトを新規作成します。
flutter create method_channel_sample
作成されているプロジェクトの lib/main.dart は以下の内容です。(コメントアウトは削除します。)
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const MyHomePage(title: 'Flutter Demo Home Page'), ); } } class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); final String title; @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text( 'You have pushed the button this many times:', ), Text( '$_counter', style: Theme.of(context).textTheme.headline4, ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: const Icon(Icons.add), ), ); } }
Android エミュレーターを起動して、アプリを実行すると、ボタンを押した回数が表示されるサンプルのコードであることがわかります。
こちらをベースに書き換えていきます。
2. Flutter 画面の作成
lib/main.dart に以下のコードを追加して、 UI を作成します。
import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const CalculatorScreen(title: 'Calculator'), ); } } class CalculatorScreen extends StatefulWidget { const CalculatorScreen({super.key, required this.title}); final String title; @override State<CalculatorScreen> createState() => _CalculatorScreenState(); } class _CalculatorScreenState extends State<CalculatorScreen> { final TextEditingController _controllerA = TextEditingController(); final TextEditingController _controllerB = TextEditingController(); String _result = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0), child: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ TextField( controller: _controllerA, keyboardType: TextInputType.number, decoration: const InputDecoration(labelText: 'a'), ), TextField( controller: _controllerB, keyboardType: TextInputType.number, decoration: const InputDecoration(labelText: 'b'), ), ElevatedButton( onPressed: _calculate, child: const Text('計算'), ), Text(_result), ], ), ), ), ); } void _calculate() async { // ここでKotlinのコードを呼び出す } }
TextEditingController
オブジェクト(_controllerA
と_controllerB
)を使って、テキストフィールドの入力値を管理します。_result
は、計算結果を格納する文字列です。_calculate
は、ElevatedButton
のonPressed
イベントで呼び出されるメソッドです。
3. MethodChannel の設定
MethodChannel を使って Flutter とネイティブプラットフォーム(Kotlin)間で通信を行う設定を行います。
3-1. Dart 側の設定
lib/main.dart に以下のコードを追加して、 MethodChannel を設定します。
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; // 追加 // 略 class CalculatorScreen extends StatefulWidget { // 略 } class _CalculatorScreenState extends State<CalculatorScreen> { // 略 // ① Method Channelを初期化する static const platform= MethodChannel('sample.flutter.dev/calculator'); @override Widget build(BuildContext context) { // 略 } // ② Method Channelを呼び出す void _calculate() async { try { final int a = int.parse(_controllerA.text); final int b = int.parse(_controllerB.text); final int result = awaitplatform.invokeMethod('add', {'a': a, 'b': b}); setState(() { _result = '結果: $result'; }); } on PlatformException catch (e) { setState(() { _result = 'エラーが発生しました: ${e.message}'; }); } } }
MethodChannel の初期化と、 Kotlin 側で定義されたメソッド(この場合はadd
メソッド)を呼び出す処理を追加しました。
3-2. Kotlin 側の設定
MainActivity クラスに MethodChannel と計算ロジックを追加します。
編集:android/app/src/main/kotlin/com/example/method_channel_sample/MainActivity.kt
package com.example.method_channel_sample import androidx.annotation.NonNull import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel class MainActivity: FlutterActivity() { private val CHANNEL = "sample.flutter.dev/calculator" override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> if (call.method == "add") { val a = call.argument<Int>("a") ?: 0 val b = call.argument<Int>("b") ?: 0 result.success(add(a, b)) } else { result.notImplemented() } } } private fun add(a: Int, b: Int): Int { return a + b } }
4. アプリの実行
アプリを実行して、ユーザーが a
と b
を入力し、「計算」ボタンを押すと a+b
の計算結果が表示されることを確認します。
おわりに
Flutter の MethodChannel を使って Kotlin のコードを実行するサンプルアプリの実装が完了しました。
Flutter は触ったばかりな上、Kotlin のコードも分からない私には難易度が高かったですが、 MethodChannel を理解しようとすることで、 Flutter 自体について理解が深まった部分もあり、とても勉強になりました。
今回の記事は以上です。ありがとうございました。
参考
#本日の自習 3/6 2h、3/7 2h
— やまて|ソフトウェアエンジニア2年目 (@r_yamate) 2023年3月7日
✅一週間の振り返り、計画作成
✅仕事でやることのキャッチアップ
向き合っていた課題の実装がようやくまとまってきた。力不足で時間がかかる。ただ、課題一つは自分でやり遂げられそうだし、Flutterへの理解が進んでるし、嬉しいことに目を向ける。
今回記事にした MethodChannel も何も分からない状態から、使い方ちょっと分かるところまでは来れた。そのことをちゃんと喜ぼうと思います。