Flutter プラットフォーム固有機能を利用するためのSystemChannels APIについて

# はじめに

Flutterからプラットフォーム固有の機能を利用するためのAPIの[SystemChannels](https://api.flutter.dev/flutter/services/SystemChannels-class.html)について解説します。

## SystemChannelsとは

まず最初に、<font color="Red">基本的にこのAPIを使うことは推奨しません</font>。Flutter Framework内部ではこのAPIを多用していますが、以下のように中間レイヤのAPIであるため今後のバージョンアップで変わる可能性が高いです。

SystemChannelsの[ソースコード](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/system_channels.dart)を見ると、中身はMethodChannel, EventChannel等を利用していることが分かると思います。MethodChannelやEventChannel等については[こちら](https://qiita.com/kurun_pan/items/db6c8fa94bbfb5c0c8d7)にまとめていますので参考にしてください。

<img width="652" alt="スクリーンショット 2020-02-11 13.16.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/259963/b899ac2f-2899-47d5-b833-b55e86911064.png">

## SystemChannelsの種類

用途毎にいくつかの種類が用意されています。一覧を以下に示します。

|種類|機能|上位のFlutter Service|
|---|---|---|
|lifecycle|ライフサイクル|widget|
|navigation|ナビゲーション|widget, system_chrome|
|system|不明…。[ここ](https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/binding.dart)で利用されているが、実質何もしていない|widget|
|accessibility|アクセシビリティ (テキスト読み上げなど)|PlatformViews, Semantics|
|platform|システム設定 (画面回転, 終了など)|SemanticsService,RouteNotificationMessages etc.|
|platform_views|プラットフォーム固有のビュー操作|AndroidView, UiKitView|
|skia|グラフィックス|-|
|keyEvent|キー入力|RawKeyEvent|
|textInput|テキスト入力|TextInput, AndroidView, UiKitView|

## サンプルコード

### lifecycle
```dart:ライフサイクルリスナー
SystemChannels.lifecycle.setMessageHandler((message){
  print('<SystemChannels.lifecycle> $message');
  /*
  AppLifecycleState.paused
  AppLifecycleState.inactive
  AppLifecycleState.resumed
  AppLifecycleState.suspending
  AppLifecycleState.detached
   */
  return Future<String>.value();
});
```

### navigation
```dart:ナビゲーション操作リスナー
SystemChannels.navigation.setMethodCallHandler((call) {
  print('<SystemChannels.navigation> ${call.method} (${call.arguments})');
  /*
   popRoute
   pushRoute
   */
  return Future<dynamic>.value();
});
```

### system
実質何にも使えず、使われておらず、無視して良さそうです。

```dart:とりあえずコールバックだけ設定
SystemChannels.system.setMessageHandler((message) {
  print('<SystemChannels.system> $message');
  return Future<dynamic>.value();
});
```

### accessibility

```dart:テキスト読み上げサンプル
SemanticsService.announce('Hello world', TextDirection.ltr)
```
のFlutter Framework内部実装が以下です。

[参照](https://api.flutter.dev/flutter/semantics/SemanticsService/announce.html)

```dart:Dart
final AnnounceSemanticsEvent event = AnnounceSemanticsEvent('Hello world', TextDirection.ltr);
SystemChannels.accessibility.send(event.toMap());
```

### platform

```dart:アプリ終了
SystemNavigator.pop()
```
のFlutter Framework内部実装が以下です。

```dart:アプリ終了
SystemChannels.platform.invokeMethod('SystemNavigator.pop');
```

### platform_views

[flutter_web](https://github.com/flutter/flutter_web/blob/master/packages/flutter_web/lib/src/widgets/platform_view.dart)で利用しているので参考にしてください。

```dart:こんな感じでビューを作成していく
final Map<String, dynamic> args = <String, dynamic>{
  'id': 1,
  'viewType': 'Create WebView',
};
SystemChannels.platform_views.invokeMethod('create', args);
```

### skia

```dart:Skiaキャッシュサイズ設定。他に機能がなく、今はこれしか出来ない…
const maxBytes = 4 * 1024 * 1024;
SystemChannels.skia.invokeMethod('setResourceCacheMaxBytes', maxBytes);
```

### keyEvent
```dart:キー入力リスナー
SystemChannels.keyEvent.setMessageHandler((message) {
  print('<SystemChannels.keyEvent> $message');
  return Future<dynamic>.value();
});
```

### textInput
```dart:キーボードの表示ON/OFF
SystemChannels.textInput.invokeMethod('TextInput.show');
SystemChannels.textInput.invokeMethod('TextInput.hide');
```