【Dart/Flutter開発】ローカルのストレージでデータ記録/管理できるHiveについてまとめた話【個人開発】

 

こんにちは、たちつてとです。

今までで、出費記録アプリについて制作してきた記事が以下3つなんですが、

【Dart/Flutter開発】表示制御と検索機能の実装、実際の(自作)アプリと考え方を共に...【スマホアプリ】

【Dart/Flutter開発】画面遷移とカレンダー機能を実装、実際の(自作)アプリと考え方を共に...【スマホアプリ】

【Dart/Flutter開発】背徳を感じつつも削除フラグを導入→しかもカッコ良くできたからヨシ!な話

いずれの記事でも、データの記録更新については

お茶を濁してきました。

だって中途半端に書くとわからないし、

全部書くととんでもない量になりそうだったので...

 

さて、ワードだけは出してきたんですが、

「Hive」なるNoSql DataBaseを使っています。

NoはNot Onlyで、否定のNoではないそうな。

今回、このHiveの記述に関するコードをまとめました。

 

更新処理とかはそんなに難しくないんですが、

データベースに登録してあるデータを取得→表示

にはまあまあ苦労したのでまとめた次第です。

Hiveを採用した理由

なぜHiveだったかというと、

どっかからサーバを引っ張ってきて

アプリを動作させるという方針が嫌だったからです。

以前、node.jsで掲示板ソフトを作ったんですが、

よそのサーバを借りて

(いわゆるリレーショナルデータベースで)

機能しています。

 

サーバは無料で利用している(heroku)んですが、

もちろん容量等の制限はあります。

正直、何十年ももつやろってレベルの使用頻度なので

問題ないんですが、なんとなく容量制限が嫌でして。

凝った機能をつける→容量を食う→要課金→癪

金払いたくないし。

 

単純に、出費を記録していくだけなので、

ローカルでなんとかならんかな

→Hiveを見つける、でした。

 

現在、趣味でアプリ開発する分には、

問題ないレベルで使用できてます。

素晴らしい!

 

Hiveのソース等

Hive Docsは、公式サイトです。

導入もきちんと書いてありますし、

僕は見なくて沼ってましたが、サンプルもあります。

ちゃんと知りたい方はぜひ一度ご覧になってください。

僕は詳細はどうでもいい派なので、

さっと見てアプリに導入→沼でした。

 

早速コード。

導入や初期処理等

まず基本的には、公式サイトにもあるように、

boxを定義してputで更新、getで取得となります。

今回一つ出費のカテゴリに関するクラスを作って、

(key, カテゴリClass(詳細情報))というような形にしてみました。

 

導入手順1

まず、以下がクラスの記述になります。

import 'package:hive/hive.dart';
@HiveType(typeId: 0)// Hiveを利用したい1個目のクラス、2個目以降も連番でOK
class Category {
@HiveField(0)// フィールドも連番でOK、基本型でないとエラー
int id = 0;
@HiveField(1)
String name = '';
@HiveField(2)
int order = 0;
@HiveField(3)
int delFlg = 0;
@HiveField(4)
String rgboStr;

Category({
this.id = -1,
this.name = '',
this.order = -1,
this.delFlg = 0,
this.rgboStr= '255_255_255_1',
});
}

↑のコメントの感じで、適宜必要な記述をしましょう。僕の場合、これで準備OKです。

 

導入手順2

次に、terminalにて、(プロジェクトのディレクトリで)

flutter pub run build_runner build

というコマンドを実行しましょう。

すると、↑の記述がしてあるファイル名.g.dartという

ファイルが生成されているはずです。

 

(僕の場合)

master_adapter.dartに記述

→master_adapter.g.dartが生成

 

ちなみに、

このコマンドを実行した後にフィールドを増やそ

となった場合も再度実行すれば、

.g.dartファイルに変更が反映されます。

 

導入手順3というか利用に必要な記述

main.dart、アプリ実行時の記述になります。

以下_categoryという変数は、一貫して同じ文字列です。

import './master_adapter.dart';
import 'package:hive/hive.dart';
import 'package:hive_flutter/hive_flutter.dart';
const category = 'category';
void main() async {
await Hive.initFlutter();// 初期処理
getCategory();
runApp(const MyApp()); // Myappを描画
}

↑main関数を非同期処理にして、

↓のgetCategoryという関数で

画面描画への準備をしています。

void getCategory() async {
Hive.registerAdapter(CategoryAdapter());// 登録処理用の前処理
var box = await Hive.openBox<Category>(category);// 登録処理用の前処理
List<Category> cates = [
Category(id: 0, name: '(; ;)削除された可能性', order: -1, rgboStr: '0_0_0_1', delFlg: 0),
Category(id: 1, name: '必須だった', order: 1, delFlg: 0),
Category(id: 2, name: '微妙に必要だった', order: 2, rgboStr: '230_230_250_1', delFlg: 0),
Category(id: 3, name: '個人的には必要だった', order: 3, rgboStr: '154_205_50_1', delFlg: 0),
Category(id: 4, name: '授業料だと思っている', order: 4, rgboStr: '85_107_47_1', delFlg: 0),
Category(id: 5, name: '失敗だった', order: 5, rgboStr: '47_79_79_1', delFlg: 0),
Category(id: 6, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 7, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 8, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 9, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 10, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 11, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 12, name: '(; ;)未使用', order: 6, delFlg: 1),
Category(id: 13, name: '(; ;)未使用', order: 6, delFlg: 1),
];

for (int i = 0; i < 14; i++) {
// ユーザがすでにデータを編集している場合は、上書きしたくない
if (box.get(i) == null) {// 登録してあるデータを取得
box.put(i, cates[i]);// データを登録
}
}
}

初回インストール時に初期データを入れたかったので、

ループさせつつif判定で

登録処理を入れてます。

  • 初回インストール→データ登録
  • 2回目以降→データ登録(上書き)はしない

※ここのCategoryは、

  • ユーザにデータを消させない
  • 名前などの編集はできる

という設計にしてあるので、

このような書きっぷりになってます。

人によっては、main関数での

このような更新処理/if判定はいらないと思います。

 

ここまでで、カテゴリを読み取る準備ができました。

読み込み処理

次はどのようにして、

先述のデータを読み取るかになります。

以下は、登録したデータ(=カテゴリ)を

読み取りたい画面での記述になります。

const category = 'category';
List<Category> _cates = [];
@override
initState() {// 画面閲覧時に起動
super.initState();
loadData();
}

void loadData() async {
List<Category> cates = [];

 // 非同期Future型で返ってくるので,then
Hive.openBox<Category>(category).then((value) {

Box<Category> mesBox = value;

  // 登録したカテゴリをforでループ
for (Category cate in mesBox.values) {
cates.add(cate)
;
  }
setState(() {// 非同期処理の最後にsetState
_cates = cates;
_asyncWidget = getContainer();
});
});

// ※ここ=「loadDataの最後」にsetStateを書いても思うように動作しない
}

基本的にはコメントにある通りです。

変数の扱いとかは結構いい加減なので許して欲しい...

ともかく大事なのは、async=非同期処理があるので、

loadDataメソッドの最後にsetStateを入れるのではなく、

非同期処理の最後にsetStateを入れないと、

想像とは異なる動作をします。

 

最後に、_catesという変数をDropDownにぶちこむなり、

一覧表示するなりすれば完成です。

これにて、Hiveを利用して

データベースに登録してあるデータを表示できました。

お疲れ様でした!

 

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

Flutter モバイルアプリ開発バイブル [ 南里勇気 ]
価格:3509円(税込、送料無料) (2021/9/20時点)

エンジニア発オンラインスクール【RUNTEQ】