Androidではこのライブラリ・APIを使うけどFlutterでは何を使うの？

# はじめに
Livesense Advent Calendar8日目を担当します @Soma-Kato です。普段は転職やバイトのAndroidアプリの開発・運用を担当しています。
今回は社外でやっているFlutterアプリ開発で、Androidではこのライブラリ・APIを使うけどFlutterでは何を使うの？みたいなことが度々あったので、それらを解決したライブラリを紹介したいと思います。
本記事内ではKotlin/Javaで開発されたAndroidアプリを`Androidネイティブ`と記述しています。

# View系
## flutter_speed_dial
タップすると複数のボタンが出てくるFABです。Googleカレンダーなどでよく使われているタップすると展開するFABですね。
Androidネイティブで実装するには少し面倒ですが、Flutterでは[flutter_speed_dial](https://pub.dev/packages/flutter_speed_dial)という便利なライブラリがあります。

![flutter_speed_dial](https://camo.githubusercontent.com/c071ddab23259d9ec0abbbe45729b2abcfc744108bf06c01e59c72f068cfa9fc/68747470733a2f2f6d656469612e67697068792e636f6d2f6d656469612f65663442706d6574767648394264514339742f67697068792e676966)

導入方法は以下の通りです。

```pubspec.yaml
dependencies:
  flutter_speed_dial: ^1.2.5
```
- FlutterでMDCのFABを扱うのと同様に、`Scaffold`の`floatingActionButton`引数に`SpeedDial`Widgetを渡すと、画面右下にFABが表示されます。
- `SpeedDial`の`animatedIcon`引数にAnimatedIconsを渡すと、トップレベルのFABのアイコンを定義できます。
- `children`引数に`SpeedDialChild`Widgetを渡すと展開されるFABのレイアウトやタップ時の挙動を定義できます。

```.dart
    return Scaffold(
      floatingActionButton: SpeedDial(
        animatedIcon: AnimatedIcons.menu_close,
        animatedIconTheme: IconThemeData(size: 22.0),
        visible: _dialVisible,
        children: [
          SpeedDialChild(
              child: Icon(Icons.accessibility),
              backgroundColor: Colors.red,
              label: 'First',
              onTap: () => print('FIRST CHILD')),
          SpeedDialChild(
              child: Icon(Icons.brush),
              backgroundColor: Colors.blue,
              label: 'Second',
              onTap: () => print('SECOND CHILD')),
        ],
      ),
    );
```

## flutter_svg
Androidネイティブ開発ではデザイナーさんに書き出してもらったSVGファイルをAndroidStudioのVector Assetで読み込んで、xmlにしてそれを参照していると思うのですが、Flutterにはその機能はありません。
しかしFlutterでは[flutter_svg](https://pub.dev/packages/flutter_svg)という、SVGを直接読み込めるライブラリがあります。

導入方法は以下の通りです。
`assets/`を定義することにより、`assets/`配下のSVGファイルをコードから参照できるようになります

```pubspec.yaml
dependencies:
  flutter_svg: ^0.18.0

flutter:
  assets:
    - assets/
```
`SvgPicture.asset`でファイル名を渡してあげると、SVGを描画できます。
`SvgPicture`自体は`StatefulWidget`なので、1つ前のセクションでお話ししたFABのアイコンなどにも使うことができます。

```dart
SvgPicture.asset('assets/icon.svg')
```


## scrollable_positioned_list
Flutter標準で実装されている`ListView`Widgetは優秀ですが、AndroidネイティブのRecyclerViewのようにListのpositionを指定して自動でスクロールされる機能はありません。(scrollToPositionのようなメソッドがありません。)
それを実現できるWidgetを提供しているライブラリが[scrollable_positioned_list](https://pub.dev/packages/scrollable_positioned_list)です。

導入方法は以下の通りです。

```pubspec.yaml
dependencies:
  scrollable_positioned_list: ^0.1.8
```

```dart
final itemScrollController = ItemScrollController();
final itemPositionsListener = ItemPositionsListener.create();

ScrollablePositionedList.builder(
  itemCount: 20,
  itemBuilder: (context, index) => Text('Item $index'),
  itemScrollController: itemScrollController,
  itemPositionsListener: itemPositionsListener,
);
```
builder自体はListViewとほとんど変わらないのですが、`ItemScrollController`と`ItemPositionsListener`を引数に渡すことができます。
`ItemScrollController`の`jumpTo(index)`がRecyclerViewの`scrollToPosition`と似たような振舞をします。

```dart
itemScrollController.jumpTo(index: 10);
```

## keyboard_avoider
Androidネイティブだと`TextField`をタップしてIMEが表示された時に、タップした`TextField`がIMEに隠れないように自動でViewを押し上げてくれると思いますが、Flutterではそのような機能がありません。
それを実現できるWidgetを提供しているライブラリが[keyboard_avoider](https://pub.dev/packages/keyboard_avoider)です。
![keyboard_avoider](https://raw.githubusercontent.com/martinrybak/keyboard_avoider/master/keyboard_avoider.gif)
導入方法は以下の通りです。

```pubspec.yaml
dependencies:
  keyboard_avoider: ^0.1.2
```
`autoScroll`引数に`true`を渡すと、`child`引数に定義されている、`TextField`がIMEに隠れないように自動でスクロールしてくれます。

```dart
KeyboardAvoider(
  autoScroll: true,
    child: Column(
      children: [
        TextFormField(),
        Container(height: 500),
        TextFormField(),
      ],
  ),
);
```

# Utility系

## json_serializable
Androidネイティブ開発で、APIのレスポンスをPOJOへ変換するために[Gson](https://github.com/google/gson)などを使うことがあると思います。
Flutterではこのような時に、[json_serializable](https://pub.dev/packages/json_serializable)ライブラリを使い、Dartクラスへ変換します。

導入方法は以下の通りです。

```pubspec.yaml
dependencies:
  json_annotation: 3.0.1

dev_dependencies:
  json_serializable: 3.3.0
```

```Person.dart
part 'Person.g.dart';

@JsonSerializable()
class Person {
  final String firstName;
  final String lastName;

  Person({this.firstName, this.lastName});
}
```
Personクラスに`@JsonSerializable`をつけ、`flutter packages pub run build_runner build`をターミナルなどのコマンドラインで実行すると、MapからPersonへ変換する`_$PersonFromJson`と、PersonからMapへ変換する`_$PersonToJson`を自動生成してくれます。

```Person.g.dart
part of 'Person.dart';

// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************

Person _$PersonFromJson(Map<String, dynamic> json) {
  return Person(
    firstName: json['firstName'] as String,
    lastName: json['lastName'] as String,
  );
}

Map<String, dynamic> _$PersonToJson(Person instance) => <String, dynamic>{
      'firstName': instance.firstName,
      'lastName': instance.lastName,
    };
```
これを先ほど定義したPersonクラスのfactoryコンストラクタなどに記述してあげると、APIレスポンスをDartクラスへ変換できるようになります。
`_$PersonToJson`を呼び出せば、Personクラスを`Map<String, dynamic>`へ変換できるので、APIにpostしたり、[sqflite](https://pub.dev/packages/sqflite)を使ってアプリ内DBに保存したりできます。

```Person.dart
...
factory Person.fromJson(Map<String, dynamic> json) =>
    _$PersonFromJson(json);

Map<String, dynamic> toJson() => _$PersonToJson(this);
```
また、[Gson](https://github.com/google/gson)の`@SerializedName`のように、`@JsonKey`を使いKey名を変更することもできます。

```Person.dart
class Person {
  @JsonKey(name: "first_name")
  final String firstName;
  @JsonKey(name: "last_name")
  final String lastName;
```

## tuple
Kotlinで関数から複数の値を返したい時などに、[Pair](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-pair/)や[Triple](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/)を使うと思いますが、Dartはそのようなクラスが実装されていません。
それを実現できるクラスを提供しているライブラリが[tuple](https://pub.dev/packages/tuple)です。

導入方法は以下の通りです。

```pubspec.yaml
dependencies:
  tuple: ^1.0.3
```
実装はとても簡単で、2つの値を持たせたい場合は`Tuple2`を使い値を渡します。
取り出す時は`item1, item2`を呼び出し値を取り出します。
`Tuple2〜Tuple７`まであるので、最大で７つの値を定義することがきます。

```dart
Tuple2<int, String> tuple2 = Tuple2(0, "1");
int number = tuple2.item1;
String name = tuple2.item2;
```

## shared_preferences
名前の通りAndroidネイティブ開発でおなじみのSharedPreferencesです。Flutterでは標準で実装されていないので、[shared_preferences](https://pub.dev/packages/shared_preferences)ライブラリを使うことになります。

導入方法は以下の通りです

```pubspec.yaml
dependencies:
  shared_preferences: 0.5.10
```
実装自体はAndroidネイティブのSharedPreferencesとほとんど同じですが、値をセットする時の戻り値が`Future<void>`になるので、非同期処理が必要になります。そういう点で言うと、AndroidXの[PreferencesDataStore](https://developer.android.com/topic/libraries/architecture/datastore)に近い感じになっています。

```dart
SharedPreferences prefs = await SharedPreferences.getInstance();
int counter = (prefs.getInt('counter') ?? 0) + 1;
print('Pressed $counter times.');
await prefs.setInt('counter', counter);
```

# デバック系
## flutter_stetho
Androidネイティブにもある[Stetho](https://pub.dev/packages/flutter_stetho)ライブラリです。
NetworkInspectorやDatabaseInspectorなどが利用できます。
Stethoを使ったことがある人はご存知だと思いますが、AndroidStudio4.1から実装されているDatabaseInspectorとは違い、StethoはDatabaseのテーブルに直接変更を加えることができません。あくまで参照するくらいしかできません。
なので個人的にはちょっと不便だなと思っていますが、FlutterでDatabaseInspector機能があるライブラリが他にない（知らない）ので利用しています。
![network_inspector (2).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152344/70bda572-c9c1-91ad-c78d-b32d9bbe0adb.gif)


導入方法は以下の通りです

```pubspec.yaml
dependencies:
  flutter_stetho: ^0.5.2
```
main関数内で初期化をし、`chrome://inspect`を開けば、いつものStethoの画面を開くことができます。

```dart
void main() {
  Stetho.initialize();

  runApp(new MyApp());
}
```

## alice
Androidネイティブ開発でよく使われるNetworkInspectorの[Chuck](https://github.com/jgilfelt/chuck)と同じようなことができるライブラリです。
StethoはChromeブラウザで開きますが、[alice](https://pub.dev/packages/alice)はアプリ内で通信ログを確認できるのでとても便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/152344/49c39cdb-4a4d-46b4-c1f5-c887f8bcc044.png)

導入方法は以下の通りです

```pubspec.yaml
dependencies:
  alice: ^0.1.6
```
実装は少し長いので省略しますが、[Example](https://pub.dev/packages/alice/example)を見ていただければ導入できると思います。

# まとめ
今回はAndroidではこのライブラリ・APIを使うけどFlutterでは何を使うの？を中心としたライブラリ紹介をさせていただきました。
個人的にFlutterのライブラリは、AndroidやiOSのネイティブ開発に触発されたものが多くあるように感じています。なので、大体必要なものは揃っていると思います。
弊社内でもFlutterを導入するタイミングを見計っています:eyes:

# 参考・画像参照
- https://pub.dev/packages/flutter_speed_dial
- https://pub.dev/packages/flutter_svg
- https://pub.dev/packages/scrollable_positioned_list
- https://pub.dev/packages/keyboard_avoider
- https://pub.dev/packages/json_serializable
- https://pub.dev/packages/tuple
- https://pub.dev/packages/shared_preferences
- https://pub.dev/packages/flutter_stetho
- https://pub.dev/packages/alice