概要

Flutterアプリにジオフェンシング機能を実装した際の備忘録 Dart編です。
Android・iOSを対象とし、デスクトップやWebは対象外です。細かな実装部分については、各公式サイトの紹介にとどめています。

実装ステップ

  1. Androidにジオフェンシング機能を実装する
  2. iOSにジオフェンシング機能を実装する
  3. Dartからジオフェンシング機能を開始/終了する
  4. AndroidからDartにジオフェンシングin/outを通知する
  5. iOSからDartにジオフェンシングin/outを通知する
  6. 位置情報使用の説明画面を作成する
  7. ストアリリースの準備をする

Dartからジオフェンシング機能を使用する

part1part2で入れた実装をDart側から使用していきます。

MethodChannelの定義

Android・iOSで実装したコードをDartから呼ぶため、今回はMethodChannelを使用します。
MethodChannelの詳細説明は省きますが、Dart側/Android・iOS側それぞれで共通の名前・メソッド名を定義することでDart側から処理を呼び出せる便利な仕組みです。
私が定義していたメソッドは下記です。

  1. initializeService
  2. registerGeofence
  3. removeGeofence
  4. getRegisteredGeofenceIds
  5. hasFineLocationPermission

もし現在位置に合わせて更新するような仕組みを組む場合、startLocationUpdateCheckstopLocationUpdateCheckといったメソッドも必要になると思います。

initializeService

ここではキル状態でジオフェンスを検知した時の処理について設定します。 Android・iOS側へ、呼び出す関数の情報を引き渡さないといけません。具体的には、関数をCallbackHandleに変換した識別子を渡します。

  • Dart側: callbackHandleを作成しinitializeServiceを呼び出す。下記、参考実装です。
MethodChannel? backgroundChannel;

@pragma('vm:entry-point')
void callbackDispatcher() {
  backgroundChannel = const MethodChannel("jp.co.example/geofence_background");
  WidgetsFlutterBinding.ensureInitialized();
  backgroundChannel?.setMethodCallHandler((call) async {
    final List<dynamic> args = call.arguments;
    final callback = PluginUtilities.getCallbackFromHandle(
        CallbackHandle.fromRawHandle(args[0]));
    callback?.call(call);
  });
  backgroundChannel?.invokeMethod('initialized');
}

class Example {
    static const MethodChannel _channel = MethodChannel("jp.co.example/geofence");
    // handlerはトップレベル関数またはstatic関数を渡す必要があります。
    void run(void Function(MethodCall call) handler) async {
        final callback = PluginUtilities.getCallbackHandle(callbackDispatcher)?.toRawHandle();
        final callbackHandle = PluginUtilities.getCallbackHandle(handler)?.toRawHandle();
        if (callback == null || callbackHandle == null) {
            // エラーハンドリング..
            return;
        }
        await _channel.invokeMethod('initializeService', [callback, callbackHandle]);
    }
}
  • Android・iOS側: initializeServiceでcallbackHandleを永続化

registerGeofence

ジオフェンスを登録する機能を呼び出します。GeofenceRegionにID、緯度、経度、半径、トリガー等の情報を詰め込みます。

static Future<void> registerGeofence(GeofenceRegion region) async {
    final args = region.toArgs();
    try {
      await _channel.invokeMethod('registerGeofence', args);
    } catch (e) {
      debugPrint(e.toString());
    }
}

removeGeofence

ジオフェンスを削除する機能を呼び出します。


static Future<bool> removeGeofence(String id) async => await _channel
      .invokeMethod('removeGeofence', <dynamic>[id]);

getRegisteredGeofenceIds

登録済みジオフェンスのID一覧を取得します。

hasFineLocationPermission

正確な位置情報をユーザーが許可しているかどうかを取得します。
実装当時permission_handlerを使用していたのですが、正確な位置情報の権限取得状況が取れなかったため、定義していました。今はもう取れるかもしれません。

Dart側から機能を呼び出す場合の処理はこんなものです。肝はAndroid・iOS側になるので、かなりシンプルですね。