本エントリではhive
を使ったデータ管理方法を説明する。
hiveについて
特徴
hive
は以下の特徴をもつデータベース。
- 動作が軽量
- キーバリュー型
- boxという単位でデータを管理
パフォーマンス
hive
の公式リポジトリにはsqflite
、shared_preferences
とのパフォーマンスの比較が掲載されている。
1000回連続でデータ読み取り、生成を行なった結果が上画像。
いずれもhive
はその他のパッケージと比べて処理時間が短く
この操作ではパフォーマンスが良いことが示されている。
公式リンク
基本的な使用方法
実装の流れ
hive
を使用する基本的な流れは以下。非常にシンプル。
pubspec.yaml
を更新してインストールHive.initFlutter()
でhive
にデータの保存先を認識させるHive.openBox( boxの名前 )
でboxを開くValueListenableBuilder
でboxを使用したいウィジェットをラップput()
やget()
などのメソッドでデータを処理
以降上から順に説明する。
また説明用として公式ドキュメントのFavorite Booksアプリを用いる。 Favorite Booksのリポジトリはこちら。各説明では必要な箇所のみを抜粋した。
pubspec.yamlを更新してインストール
hive
はサードパーティのパッケージなので使用する際にはpubspec.yaml
に以下を記述する。
dependencies: hive: ^2.2.3 hive_flutter: ^1.1.0
Hive.initFlutter()でhiveにデータの保存先を認識させる
void main() async { await Hive.initFlutter(); runApp(MyApp()); }
main()
関数内でアプリ起動直後に実行していることが多い。
この処理でhive
にデータの保存先を認識させる。
Hive.openBox( boxの名前 )でboxを開く
const favoritesBox = 'favorite_books'; void main() async { await Hive.initFlutter(); await Hive.openBox<String>(favoritesBox); runApp(MyApp()); }
この処理もmain()
関数内でアプリ起動直後に実行していることが多い。
openBox()
の引数にはboxの名称が入る。boxの名称は何度も使用するので定数として定義すると楽。
ValueListenableBuilder でboxを使用したいウィジェットをラップ
class MyApp extends StatefulWidget { .. } class _MyAppState extends State<MyApp> { late Box<String> favoriteBooksBox; @override void initState() { super.initState(); favoriteBooksBox = Hive.box(favoritesBox); } ... @override Widget build(BuildContext context) { return MaterialApp( ..., home: Scaffold( ..., body: ValueListenableBuilder( valueListenable: favoriteBooksBox.listenable(), builder: (context, Box<String> box, _) { return ListView.builder( itemCount: books.length, itemBuilder: (context, listIndex) { return ListTile( ... ); }, ); }, ), ), ); } }
ValueListenableBuilder
でboxが必要なウィジェットをラップする。
valueListenable
にはValueListenable<Box<T>>
型のインスタンスを渡す必要があるので、listenable()
メソッドで作成する。
実際にboxからデータを使用するにはbuilder
プロパティ内で匿名関数の第二引数boxを利用する。
put()やget()などのメソッドでデータを処理
class _MyAppState extends State<MyApp> { Widget getIcon(int index) { if (favoriteBooksBox.containsKey(index)) { return Icon(Icons.favorite, color: Colors.red); } return Icon(Icons.favorite_border); } void onFavoritePress(int index) { if (favoriteBooksBox.containsKey(index)) { favoriteBooksBox.delete(index); return; } favoriteBooksBox.put(index, books[index]); } @override Widget build(BuildContext context) { return MaterialApp( ... home: Scaffold( ... body: ValueListenableBuilder( valueListenable: favoriteBooksBox.listenable(), builder: (context, Box<String> box, _) { return ListView.builder( itemCount: books.length, itemBuilder: (context, listIndex) { return ListTile( title: Text(books[listIndex]), trailing: IconButton( icon: getIcon(listIndex), onPressed: () => onFavoritePress(listIndex), ), ); }, ); }, ), ), ); } }
boxで管理しているデータを利用するにはput()
、get()
、delete()
プロパティを使用する。
それぞれの記述方法は以下。
put()
boxに値を挿入する。もし既にキーが存在する場合は値が更新される。 文法は以下の通り。
boxインスタンス.put(キー名称, 値);
favorite booksアプリでは以下の箇所で使用している。
void onFavoritePress(int index) { if (favoriteBooksBox.containsKey(index)) { favoriteBooksBox.delete(index); return; } favoriteBooksBox.put(index, books[index]); }
公式ドキュメントの説明は以下。
get()
boxからキー名称で指定した値を取得する。キーが存在しない場合はnull
が返される。
文法は以下の通り。
boxインスタンス.get(キー名称);
favorite booksアプリでは使用していない。 公式ドキュメントの説明は以下。
delete()
box内のキー名称で指定した値を削除する。
boxインスタンス.delete(キー名称);
favorite booksアプリでは以下の箇所で使用している。
void onFavoritePress(int index) { if (favoriteBooksBox.containsKey(index)) { favoriteBooksBox.delete(index); return; } favoriteBooksBox.put(index, books[index]); }
公式ドキュメントの説明は以下。
ユーザー定義クラスを用いる際の使用方法
hive
を用いてユーザー定義クラスを扱う場合、TypeAdapter
を用いてクラスをhive
が認識できるようにする必要がある。
このTypeAdapter
の実装はフルスクラッチで書くこともできるが、一般的にはbuild_runner
を用いた実装の方が楽。
本エントリではbuild_runner
を用いた実装を説明する。手順は以下。
pubspec.yaml
更新- ユーザー定義クラスに追記
build_runner
の実行- ユーザー定義クラスを
hive
へ登録
pubspec.yaml更新
hive_generator
とbuild_runner
をインストールする。
インストール時には最新バージョンを確認。
dev_dependencies: ... hive_generator: ^1.1.1 build_runner: ^2.1.5
ユーザー定義クラスに追記
以下のユーザー定義クラスを例に説明する。
class Person { String name; int age; List<Person> friends; }
Person
クラスをhive
に認識させるため以下のように記述する。
import 'package:hive/hive.dart'; part 'person.g.dart'; @HiveType(typeId: 1) class Person { @HiveField(0) String name; @HiveField(1) int age; @HiveField(2) List<Person> friends; }
このコードのポイントを説明を行う。
partキーワード
part 'person.g.dart';
part
はファイルを分割する際に用いられるキーワードで、person.g.dart
がperson.dart
の一部だという意味。
person.g.dart
はbuild_runner
を実行した際に自動で作成されるファイル。
一般的にbuild_runner
で自動作成(generate)されたファイル名には```.g````が付く。
@HiveType(typeId: )
@HiveType(typeId: 1)
はこのユーザー定義クラスをhive
が認識するためのキーワード。
クラス名の先頭につける。
このように@
で始まるキーワードはannotationと呼ばれる。
typeId
プロパティに渡す数字はこちら,-Annotate%20all%20fields)に記載されている通り、0~223の数値を渡す。この数値は他のクラスと重複してはダメなので、hive
に登録できるユーザー定義クラスは224個という制限がある。
@HiveField()
@HiveField(0)
はユーザー定義内のプロパティにそれぞれ付けるannotaiton。
括弧内に渡す数値はプロパティごとにクラス内でユニークであれば良いとのこと。
build_runnerの実行
ターミナルなどのCLIで以下のコマンドを実行する。
flutter packages pub run build_runner build
これでperson.g.dart
がperson.dart
と同じディレクトリに生成される。
ユーザー定義クラスをhiveへ登録
Hive
クラスのメソッドregisterAdapter()
を用いてPerson
クラスをhive
に認識させる。
次のようにmain()
の中で実行されることが多い。
Future<void> main() async { await Hive.initFlutter(); Hive.registerAdapter(PersonAdapter()); ... }
以上でユーザー定義クラスをhive
で扱うことができる。
hive以外のデータ管理パッケージ
Flutterにはhive
以外にもデータを永続化して管理する手段がある。
以下のパッケージがhive
以外の候補として上がることが多い。
- sqflite
- shared_reference
これらの使用方法などについては以下のエントリでまとめている。