LoginSignup
1
0

More than 5 years have passed since last update.

[WIP] Flutter - Writing custom platform-specific code with platform channels (日本語訳)

Last updated at Posted at 2018-09-11

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 ホストを開きます:

  1. Android Studio を開く
  2. ‘File > Open…’ メニューを選択
  3. Flutter アプリのあるディレクトリに行き、その中の android フォルダーを選択し、OK をクリック
  4. 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

書きかけ

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0