FlutterでHiveをいい感じに使う

# 基本実装

shared_preferencesを使っていたのだが、Hiveというpackageがあったのでこちらを使用してみました
shared_preferencesとは異なり様々な型が保存でき、速度も速いということで利便性はかなり高いかと思います

こちらが公式ページ

https://pub.dev/packages/hive

インストール用のコマンド
```
flutter pub add hive
flutter pub add path_provider
```

keyの管理をenumを使って行う
```
enum HiveBox {
  string,
  int,
  double,
  bool,
  dynamic,
  listString,
  map,
}
```
Hiveを利用するためにインスタンスを宣言する
何度も取得する必要はないので起動時にでもinitializeHiveを読んであげればいいかと思います
また、Hiveには保存場所を指定することができるのでpath_providerを使用して指定します
```
/// Hiveのインスタンス
late Box<dynamic> _box1;

/// Hiveの初期化処理
Future<void> initializeHive() async {
  // Hiveの保存先を指定
  final dir = await getApplicationDocumentsDirectory();
  // Hiveの初期化
  Hive.init(dir.path);
  // Hiveのインスタンス取得
  _box1 = await Hive.openBox<dynamic>('HiveBox');
}
```

keyの管理をしているenumを拡張することでいろんな処理をできるようにする
```
extension HiveBoxExtension on HiveBox {
  // ここに処理を追加していく
}
```

Hiveは様々な型を使用できるため、逆にどの型を使用しているかわからなくなることが想定されるので指定してあげる
```
  /// 型の指定
  dynamic get type {
    switch (this) {
      case HiveBox.string:
        return String;
      case HiveBox.int:
        return int;
      case HiveBox.double:
        return double;
      case HiveBox.bool:
        return bool;
      case HiveBox.dynamic:
        return dynamic;
      case HiveBox.listString:
        return List<String>;
      case HiveBox.map:
        return Map<String, dynamic>;
    }
  }
```

値の設定と取得の処理は下記のようになります
Genericsを利用することdynamicではなく特定の型で取得できます
また、上記で指定した型と一致する場合のみ処理を行うようにすると想定外の動作を避けれるかと思います
```
  /// 値設定
  Future<void> put<T>(T value) async {
    if (T == type) {
      await _box1.put(name, value);
    }
  }

  /// 値取得
  T? get<T>() {
    try {
      if (T == type) {
        return _box1.get(name) as T?;
      } else {
        return null;
      }
      // ignore: avoid_catching_errors
    } on TypeError {
      return null;
    }
  }
```

空チェックや削除ができた方が使いやすいと思うのでこちらも実装します
```
  /// 空チェック
  bool get contains => _box1.containsKey(name);

  /// 削除
  Future<void> get delete => _box1.delete(name);
```

Hiveには特定のkeyだけではなく全体に対して処理することできるので、必要であればこちらも実装してください
```
  /// mapとして全件取得
  static Map<dynamic, dynamic> get toMap => _box1.toMap();

  /// keyのリストを取得
  static Iterable<dynamic> get keys => _box1.keys;

  /// valueのリストを取得
  static Iterable<dynamic> get values => _box1.values;

  /// 全件削除
  static Future<void> get clear => _box1.clear();
```

単体テストは下記のようになります
かなり見やすく使いやすいかと思います
```
void main() {
  setUpAll(() async {
    TestWidgetsFlutterBinding.ensureInitialized();
    // unitテストではpathの指定が失敗するため設定する
    const MethodChannel('plugins.flutter.io/path_provider_macos')
        .setMockMethodCallHandler((methodCall) async => '.');
    // Hiveの初期化処理の実行
    await initializeHive();
  });

  test('string Hive test', () async {
    // 試験用に削除する
    await HiveBoxExtension.clear;

    // 空チェック
    expect(HiveBox.string.contains, false);
    expect(HiveBox.string.get<String>(), null);

    // 値セット
    await HiveBox.string.put('テスト');
    expect(HiveBox.string.contains, true);
    expect(HiveBox.string.get<String>(), 'テスト');

    // 値削除
    await HiveBox.string.delete;

    // 空チェック
    expect(HiveBox.string.contains, false);
    expect(HiveBox.string.get<String>(), null);
  });
}
```

# 応用
独自の型も保存できるのでこちらも対応していく

freezedを使用するのでこちらも参考にしてください
今回は詳しく説明しません

https://qiita.com/slovenly_engineer/items/a9ecea3d8e1731b8d99e

Hiveのインストール用のコマンド
```
flutter pub add hive_flutter
flutter pub add --dev hive_generator
```

freezedとHiveを組み合わせたデータクラスの宣言は下記になります
freezedを使用したことがある人はわかると思いますが、```@HiveType(typeId: n)、@HiveField(n)```だけ追加されてます
試してないのですが```@HiveType(typeId: n)```は0~233くらいまで設定できるみたいです
独自の型を設定できるのは最大234個くらいみたいです
```@HiveField(n)```は0から順番に設定してあげてください
```
@HiveType(typeId: 0)
@freezed
class User with _$User {
  const factory User({
    @HiveField(0) required String name,
    @HiveField(1) required int age,
  }) = _User;

  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
```

ちなみにHiveに反映させるには初期化処理に下記を追加してください
```
  Hive
    ..init(dir.path)
    ..registerAdapter(UserAdapter());
```

あとは上記の基本実装で使えます
enumの追加も忘れずに・・・

# 類似
https://qiita.com/slovenly_engineer/items/6a5bc025808eba663c75