Pei's Lab

超入門 Hiveによるデータ保存

hiveを使ってデータを永続化してみます。今回は超入門ということで、flutterの新規プロジェクト生成時に作成されるデモアプリにhiveを組み込んでいきます。
flutterの学習はこちらもおすすめ


データベース選定

データベースの選定にあたってはこちらのサイトを参考にしました。

筆者によると

とのことです。Firebaseはgoogle製なのでflutterのドキュメントでも説明されていたり、ユーザーが多かったりと良い所もあります。しかし、簡単に調査してみたところやはり設定はそこそこ面倒で、初めて使うデータストアとしてはちょっと敷居が高そうに感じました。ただし、多くのアプリを作るうえでwebサポートは必要になると思います。これらを踏まえて今回はHiveを選択しました。

flutterの開発環境構築は済んでいると想定し、コーダーはVSCodeを使用します。

成果物

今回はこのようなものを作ってみようと思います。

実装

プロジェクト作成

Ctrl+Shift+Pでコマンドパレットを開きflutterと入力したのちにNew Projectを選択します。

任意のプロジェクト名とプロジェクトの保存場所を指定します。今回はhive_studyとしました。

flutterアプリの基本構成が出来上がり、いくつかのフォルダ及びファイルが生成されます。

F5を押下して実行するとエミュレータが起動し、デモアプリが起動します。

このアプリは右下のボタンをタッチすると中央の数字がカウントアップされるという非常にシンプルなものです。ただし、数字は永続化されていないため、アプリを再起動すると”0″に戻ってしまいます。

Hiveのインポート

そこで、今回はHiveを使ってデータを永続化し、アプリの再起動後は前回の数字が表示されるようにしてみようと思います。加えてリセットボタンも配置してみます。

// pubspec.yaml
dependencies:
  flutter:
    sdk: flutter
  hive: ^1.4.1+1           // 追加
  hive_flutter: ^0.3.0     // 追加

追記したら保存します。VSCodeの場合は保存と同時に”pub get”コマンドが実行され、hive及びhive_flutterを使用する準備が整います。もしうまくコマンドが実行されていないようであれば、ターミナルを用いて

>flutter pub get

を実行してください。
main.dartに戻り、hiveをインポートします。

// main.dart
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';                       // 追加
import 'package:hive_flutter/hive_flutter.dart';       // 追加

これでhiveを使用する準備は完了です。

データの読み書き

それでは実装を進めていきましょう。main関数の初めに、Hiveの初期化とboxの準備をします。

// main.dart
void main() async {               // 修正
  await Hive.initFlutter();       // 追加
  await Hive.openBox('datas');    // 追加
  runApp(MyApp());
}

Hiveは非同期処理を行うのでmain関数にasyncを追加し、Hiveのメソッドはawaitを付けます。

Hive.initFlutterはデータの保存場所を調べて設定しています。これのおかげでiOS、Androidの両方で最適なディレクトリを簡単に設定することができます。これはhive_flutterに含まれている機能です。

Hive.openBoxでは指定した名前で保存されているデータをメモリに展開します。最初にメモリに展開してからアクセスすることで高速化が実現に繋がっています。今回はdatasとしましたが、名前は何でも大丈夫です。

そもそも”Box”とは何かというと、公式サイトではこう書かれています。

All data stored in Hive is organized in boxes. A box can be compared to a table in SQL, but it does not have a structure and can contain anything.

つまり“Box”はSQLで言うところのテーブルに近い概念ですが、テーブルとは異なりフィールドの形式をあらかじめ決めることはしません。キーバリューペアであればなんでも入れることができます。また、バリューには複雑なオブジェクトを入れることも可能です。まさに何でも入れられるただの”箱”ということですね。

コードを見ながらデータの読み込み、書き出しの方法を確認しましょう。

// main.dart
class _MyHomePageState extends State<MyHomePage> {
  int _counter = Hive.box('datas').get('counter', defaultValue: 0);  // 修正 データの読み込み

  void _incrementCounter() {
    setState(() {
      // This call to setState tells the Flutter framework that something has
      // changed in this State, which causes it to rerun the build method below
      // so that the display can reflect the updated values. If we changed
      // _counter without calling setState(), then the build method would not be
      // called again, and so nothing would appear to happen.
      _counter++;
      Hive.box('datas').put('counter', _counter);    // 追加 _counterを保存
    });
  }

Have.boxはopenBox済みのBoxを指定するメソッドです。またBox内のデータを取り出す時はget、保存するときはputを使用します。

Hive.box(‘datas’).get(‘counter’)によって、メモリに展開済みのdatasというBoxからcounterというキーで保管されているデータを取り出すという操作ができます。

getではdefaultValueを設定することもできます。今回の例で言えば、初回起動時はまだcounterというキーが保存されていないため、get(‘counter’)とするとNULLが返ってきます。このようなときにNULLの代わりに何を返すかをdafaultValueで設定できます。今回に限って言えば、NULLなら自動的に0になるので必ずしも書く必要はありませんが一応0を設定しておきます。

_counter++でカウンターが更新されたら値を保存します。.put(‘counter’, _counter)によってcounterというキーで_counterの値を保存しています。

これで読み書きの処理は完成です。ホットリロードはうまく動作しない場合もあるので、一度F5でコンパイルし直すのがよいと思います。改めてアプリが立ち上がるとボタンを押すたびに_counterの値が保存されるようになっています。一度アプリを閉じて再起動すると、前回終了時のカウントから再開することが確認できます。

データの削除

続いて、リセットボタンを作成して保存済みのデータの削除を行います。カウントされている数字の下にリセット用のアイコンを配置してみましょう。

_MyHomePageStateクラスのbuild関数内に表示内容が書いてあります。IconButtonを追加し、ボタンのイベントとして_resetCounterを設定します。

// main.dart
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
            IconButton(                   // 4行追加
              icon: Icon(Icons.delete),
              onPressed: _resetCounter
            ),
          ],

_MyHomePageStateクラス内に_resetCounter関数も実装します。

// main.dart
  void _resetCounter() {
    setState(() {
      Hive.box('datas').delete('counter');
      _counter = 0;
    });

box内のデータの削除にはdeleteを使用します。データを削除しただけでは_counterの値は変わらないので、別途初期化が必要です。

setStateでラップすることで表示が更新されます。setStateをコメントアウトするとデータは書き換わっているのですが、表示は更新されないという状態になります。

まとめ

Hiveを使用したデータの永続化について説明してきました。基本的な流れは理解できたかと思います。

今回は文字列のキーと数値のバリューからなる非常に単純なキーバリューペアを見てきましたが、Hiveの神髄はバリューにカスタムオブジェクトを設定できるところにあると思います。もっといろいろなデータを保存したくなった場合は公式ドキュメントをのぞいてみてください。

Description
Hive Docs - docs.hivedb.dev