【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を利用して
データベースに登録してあるデータを表示できました。
お疲れ様でした!
|
|