Writing custom platform-specific code with platform channels の日本語訳
Writing custom platform-specific code with platform channels
このガイドはプラットフォーム固有のコードを書く方法を説明します。いくつかのプラットフォーム固有の機能は既存のパッケージで利用可能です。using packages を参照してください。
Flutter は柔軟なシステムを採用しています。それは Android では Java か Kotorin で、iOS では ObjectiveC か Swift で書かれたプラットフォーム固有の API を呼び出します。
Flutter の platform-specific API support はコードジェネレーションに頼りません。フレキシブルなメッセージパッシングスタイルを用います:
- あなたの Flutter アプリはプラットフォームチャネルを通して host (iOS か Android )にメッセージを送信します。
- host はプラットフォームチャネルをリッスンし、メッセージを受け取ります。そして platform-specific APIs をいくつでも呼び出します(ネイティブプログラミング言語を使って)。そして client (Flutter アプリ)にレスポンスを返します。
Architectural overview: platform channels
以下の図のように、メッセージは client と host の間でプラットフォームチャネルを使ってやりとりされます。
Messages and responses are passed asynchronously, to ensure the user interface remains responsive.
メッセージとレスポンスはユーザーインターフェイスを止めないために非同期にやりとりされます。
クライアントサイドでは MethodChannel (API) メッセージを送信し、それはメソッドコールに対応します。プラットフォームサイドでは MethodChannel (Android) (API) と FlutterMethodChannel (iOS) (API) がメソッドコールを受信し、結果を返します。これらのクラスがとても少ない "ボイラープレート" コードでプラットフォームプラグインを開発することを可能にします。
Note: 必要であれば、プラットフォームがクライアントとして Dart で実装されたメソッドに対して逆向きにメソッドコールを送信することもできます。具体例: quick_actions。
Platform channel data types support and codecs
標準のプラットフォームチャネルは単純な JSON-like な値(booleans, numbers, Strings, byte buffers, and List and Maps of these (see StandardMessageCodec) for details))の効率的なバイナリシリアライゼーションをサポートする標準のメッセージコーデックを使用します。これらの値のシリアライゼーションとデシリアライゼーションはメッセージの送信と受信のときに自動で行われます。
以下のテーブルが Dort value がどのようにプラットフォームサイドまたはその逆で受け取られるかを表しています:
Dart | Android | iOS |
---|---|---|
null | null | nil (NSNull when nested) |
bool | java.lang.Boolean | NSNumber numberWithBool: |
int | java.lang.Integer | NSNumber numberWithInt: |
int, if 32 bits not enough | java.lang.Long | NSNumber numberWithLong: |
double | java.lang.Double | NSNumber numberWithDouble: |
String | java.lang.String | NSString |
Uint8List | byte[] | FlutterStandardTypedData typedDataWithBytes: |
Int32List | int[] | FlutterStandardTypedData typedDataWithInt32: |
Int64List | long[] | FlutterStandardTypedData typedDataWithInt64: |
Float64List | double[] | FlutterStandardTypedData typedDataWithFloat64: |
List | java.util.ArrayList | NSArray |
Map | java.util.HashMap | NSDictionary |
Example: Calling platform-specific iOS and Android code using platform channels
以下は、現在のバッテリーレベルを取得および表示するためにどのよに platform-specific API を呼び出すかのデモです。それはプラットフォームメッセージ getBatteryLevel を通じて Android BatteryManager API と iOS device.batteryLevel API を利用します。
この例はプラットフォーム固有のコードを main app 自体に追加しています。もしあなたがプラットフォーム固有の処理を他のアプリでも再利用したいならプロジェクト作成のステップは少し違ってきますが(参照 developing packages)、プラットフォームチャネルのコードは変わりません。
Note: 完全な実行可能なソースコードはこちらを参照してください。 Android with Java と iOS with Objective-C /examples/platform_channel/、iOS with Swift /examples/platform_channel_swift/。
Step 1: Create a new app project
新規アプリの作成:
- ターミナルで実行:
flutter create batterylevel
デフォルトでテンプレートは Android では Java、iOS では Objective-C を使います。Kotlin や Swift を使うためには -i
や -a
フラグを使います。
- ターミナルで実行:
flutter create -i swift -a kotlin batterylevel
Step 2: Create the Flutter platform client
アプリの State
クラスが現在のアプリのステートを保持します。これを現在のバッテリーの状態を保持できるように拡張する必要があります。
まず、チャネルを作ります。バッテリーレベルを返す一つのプラットフォームメソッドをともなう MethodChannel
を使います。
チャネルのクライアントとホストはチャネルのコンストラクタで指定したチャネル名によって接続されます。全てのチャネル名は一つのアプリの中でユニークでなければなりません。ユニークドメインプレフィックスをチャネル名の先頭に付けることを推奨します。例: samples.flutter.io/battery
。
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
...
class _MyHomePageState extends State<MyHomePage> {
static const platform = const MethodChannel('samples.flutter.io/battery');
// Get battery level.
}
次に、具体的なメソッドを String identifier getBatteryLevel
を通じて指定することでメソッドチャネルのメソッドを実行します。もし例えばプラットフォームがそのプラットフォーム API をサポートしていなければメソッドコールは失敗します(シミュレーター上で動かしている時など)。なので invokeMethod
を try-catch で囲みます。
setState の中で戻り値を使って UIステート _batteryLevel を更新します。
// Get battery level.
String _batteryLevel = 'Unknown battery level.';
Future<Null> _getBatteryLevel() async {
String batteryLevel;
try {
final int result = await platform.invokeMethod('getBatteryLevel');
batteryLevel = 'Battery level at $result % .';
} on PlatformException catch (e) {
batteryLevel = "Failed to get battery level: '${e.message}'.";
}
setState(() {
_batteryLevel = batteryLevel;
});
}
最終的に、テンプレートがバッテリーの状態を文字列で表示する小さなユーザーインターフェイスを含むように、また値を更新するボタンを含むように build
メソッドを以下のように置き換えます。
@override
Widget build(BuildContext context) {
return Material(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
RaisedButton(
child: Text('Get Battery Level'),
onPressed: _getBatteryLevel,
),
Text(_batteryLevel),
],
),
),
);
}
Step 3a: Add an Android platform-specific implementation using Java
Note: 以下のステップは Java を使います。Kotlin が良ければ 3b までスキップしてください。
Android Studio であなたの Flutter アプリの Android ホストを開きます:
- Android Studio を開く
- ‘File > Open…’ メニューを選択
- Flutter アプリのあるディレクトリに行き、その中の
android
フォルダーを選択し、OK をクリック - Project ビューで java フォルダーの中の
MainActivity.java
を開く
次に、 MethodChannel
を作り onCreate
メソッドの中で MethodCallHandler
をセットする。Flutter のクライアント側で使ったのと同じチャネル名を使うようにしてください。
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
public class MainActivity extends FlutterActivity {
private static final String CHANNEL = "samples.flutter.io/battery";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(
new MethodCallHandler() {
@Override
public void onMethodCall(MethodCall call, Result result) {
// TODO
}
});
}
}
次に、実際に バッテリーレベルを取得するために Android battery APIs を呼び出す Java コードを追加します。このコードはあなたかネイティブ Android アプリのために書くであろうコードとまったく同じです。
まず、ファイルの先頭に必要な import を追加します:
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.BatteryManager;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
そしてアクティビティクラスの onCreate
メソッドの下に以下のメソッドを追加します:
private int getBatteryLevel() {
int batteryLevel = -1;
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
BatteryManager batteryManager = (BatteryManager) getSystemService(BATTERY_SERVICE);
batteryLevel = batteryManager.getIntProperty(BatteryManager.BATTERY_PROPERTY_CAPACITY);
} else {
Intent intent = new ContextWrapper(getApplicationContext()).
registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
batteryLevel = (intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1) * 100) /
intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
}
return batteryLevel;
}
最後に、先に追加した onMethodCall
メソッドを完成させます。シングルプラットフォームメソッド getBatteryLevel
を処理する必要があるため、call 引数でそれをテストします。実装は前のステップで書いた Android コードを単に呼び出します。そして response
引数を使って成功とエラーのためにレスポンスを返します。もし不明なメソッドが呼ばれた場合、それを伝えます。以下を
public void onMethodCall(MethodCall call, Result result) {
// TODO
}
以下のように書き換えてください:
@Override
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getBatteryLevel")) {
int batteryLevel = getBatteryLevel();
if (batteryLevel != -1) {
result.success(batteryLevel);
} else {
result.error("UNAVAILABLE", "Battery level not available.", null);
}
} else {
result.notImplemented();
}
}
これでアプリを Android で動かすことができるはずです。もし Android エミュレータを使っているなら、ツールバーの ...
からアクセスできる Extended Controls panel からバッテリーレベルをセットできます。
Step 3b: Add an Android platform-specific implementation using Kotlin
書きかけ