Flutter上で大量のデータを管理・検索しようと思ったため、SQLiteを使ってみます。
生のSQL文を書くより、ORマッパーを使いたいと思ったため、アクティブレコードパターンっぽくレコードを扱える sqfentity ライブラリを使います。
SQLiteのDBファイルの作成、DDL文の発行などはすべてライブラリがやってくれますので、生SQLを一切触らずにアプリで利用できます。
https://pub.dev/packages/sqfentity
依存ライブラリに build_runner が入っています。このライブラリは、テーブルの定義を書いて、そこからbuild_runner でモデルコードを生成し、アプリでそれを使います。
最初は、ひと手間かかる感じが少し面倒そうだったのでコードジェネレートに少し抵抗があったのですが、生成済みのコードは機能リファレンスのように使えるため、なかなか使い勝手は良いです。
なお、DBの内容はアプリを再起動しても消えませんが、アプリをアンインストールすると消えます。
空のリポジトリを作成し、その中に新たな Flutter プロジェクトを作ります。
mkdir todo-sqf-flutter
cd todo-sqf-flutter
flutter create app
作った todo-sqf-flutter ディレクトリを、Flutter プラグイン組み込み済みの Android Studio で開きます。
右上 Add Configuration
+
→ ○ more items → Flutter
dart entry point は app/lib/main.dart
Dart SDK Not found になる場合は、 Fix → Flutterプラグインの場所を指定 (Dart でなくてよい)
虫マークをクリックして起動
カウントアップのデモアプリが起動する。
pubspeck.yaml に 追加
dependencies:
...
sqfentity: ^1.2.2+10
sqfentity_gen: ^1.2.0+8
dev_dependencies:
...
build_runner: ^1.6.5
build_verify: ^1.1.0
インストール
flutter pub get
lib/models.dart を作成
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
import 'package:sqfentity/sqfentity.dart';
import 'package:sqfentity_gen/sqfentity_gen.dart';
part 'models.g.dart';
@SqfEntityBuilder(todoDbModel)
const todoDbModel = SqfEntityModel(
modelName: 'TodoDbModel', // optional
databaseName: 'todo.db',
databaseTables: [todo],
bundledDatabasePath: null
);
const todo = SqfEntityTable(
tableName: 'todo',
primaryKeyName: 'id',
primaryKeyType: PrimaryKeyType.integer_auto_incremental,
useSoftDeleting: true,
fields: [
SqfEntityField('title', DbType.text),
SqfEntityField('active', DbType.bool, defaultValue: true),
]);
フィールドに、id (PK) と title を持つ、単純なモデルです。
(active というフィールドを作りましたがでもアプリ内では結局使いませんでした)
flutter packages pub run build_runner build --delete-conflicting-outputs
このようなモデルを含むコードが生成されます
// BEGIN ENTITIES
// region Todo
class Todo {
Todo({this.id, this.title, this.active, this.isDeleted}) {
_setDefaultValues();
}
Todo.withFields(this.title, this.active, this.isDeleted) {
_setDefaultValues();
}
Todo.withId(this.id, this.title, this.active, this.isDeleted) {
_setDefaultValues();
}
Todo.fromMap(Map<String, dynamic> o) {
id = o['id'] as int;
title = o['title'] as String;
active = o['active'] != null ? o['active'] == 1 : null;
isDeleted = o['isDeleted'] != null ? o['isDeleted'] == 1 : null;
}
...
使用前に、initializeDB をコールします。
void main() async {
final bool isInitialized = await MyDbModel().initializeDB();
if (isInitialized == true) {
runApp(MyApp());
}
}
起動します。ログは、起動時に毎回出ます
Syncing files to device iPhone 7...
flutter: init() -> modelname: null, tableName:todo
flutter: >>>>>>>>>>>>>>>>>>>>>>>>>>>> SqfEntityTableBase of [todo](id) init() successfuly
flutter: todo.db created successfully
flutter: SQFENTITIY: Table named [todo] was initialized successfuly (created table)
flutter: SQFENTITIY: The database is ready for use
flutter: SQFENTITIY: Table named [todo] was initialized successfuly (No added new columns)
flutter: SQFENTITIY: The database is ready for use
Todo(title: textEditingController.text, active: true).save();
リアクティブではないので、実施後手動でウィジェットのリビルドが必要そうです。
Todo(id: widget.todoId, title: textEditingController.text,
active: true).save();
save() メソッドは、UPSERT として機能します
Todo().select().orderByDesc('id').toList()
空引数でクラスインスタンスを作ってから絞り込んでいきます。
戻り値はFuture ですので、単純にウィジェットに表示するだけなら、 FutureBuilder や StreamBuilder 使うと良さそうです。
Todo().select().id.equals(widget.todoId).delete()
models.dart を修正し、build_runner してアプリを再起動すれば、自動的に ALTER TABLE してくれる。データは消えない。
flutter: init() -> modelname: null, tableName:todo
flutter: >>>>>>>>>>>>>>>>>>>>>>>>>>>> SqfEntityTableBase of [todo](id) init() successfuly
flutter: SQFENTITIY: alterTableQuery => [ALTER TABLE todo ADD COLUMN created datetime]
flutter: SQFENTITIY: Table named [todo] was initialized successfuly (Added new columns)
flutter: SQFENTITIY: The database is ready for use
flutter: SQFENTITIY: Table named [todo] was initialized successfuly (No added new columns)
flutter: SQFENTITIY: The database is ready for use
その他、公式APIドキュメントに豊富な機能が紹介されています。