Open5

【Flutter】内部ストレージ Drift

heyhey1028heyhey1028

依存パッケージ

dependencies

  • drift
  • sqlite3_flutter_libs
  • path_provider
  • path

dev_dependencies

  • build_runner
  • drift_dev

path_providerを使っていることからも分かるように内部的にはデバイスにファイルを作成し、それをデータベースとして取り扱っている様子。

heyhey1028heyhey1028

導入

  1. パッケージのインポート
  2. テーブルクラスを定義
  3. データベースクラスを定義
  4. データベースクラスをインスタンス化

0. パッケージのインポート

dependencies:
  drift: ^2.18.0
  sqlite3_flutter_libs: ^0.5.0
  path_provider: ^2.0.0
  path: ^1.9.0

dev_dependencies:
  drift_dev: ^2.18.0
  build_runner: ^2.4.9

1. テーブルクラスの定義

Tableクラスを継承したクラスを生成。
「カラムのデータ型」+「get」+「カラム名」+「Dartによるマッパー」の形で定義。

class TodoItems extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get title => text().withLength(min: 6, max: 32)();
  TextColumn get content => text().named('body')();
  IntColumn get category => integer().nullable()();
}

2. データベースクラスの定義

  1. _$[クラス名]を継承したクラスに
  2. @DriftDatabase(tables:[テーブルの配列])のアノテーションを付け
    • テーブルが複数ある場合はtables:[]の配列内に記述 ref
  3. schemaVersionのgetterを定義し
  4. LazyDatabaseクラスを返す処理を定義し(現時点では呪いに近い...)
  5. コンストラクタで実行
  6. 生成ファイルをpart importし
  7. build runnerで生成
database.dart
part 'database.g.dart';

(tables: [TodoItems])
class AppDatabase extends _$AppDatabase {
  AppDatabase() : super(_openConnection());

  
  int get schemaVersion => 1;
}

LazyDatabase _openConnection() {
  // the LazyDatabase util lets us find the right location for the file async.
  return LazyDatabase(() async {
    // put the database file, called db.sqlite here, into the documents folder
    // for your app.
    final dbFolder = await getApplicationDocumentsDirectory();
    final file = File(p.join(dbFolder.path, 'db.sqlite'));

    // Also work around limitations on old Android versions
    if (Platform.isAndroid) {
      await applyWorkaroundToOpenSqlite3OnOldAndroidVersions();
    }

    // Make sqlite3 pick a more suitable location for temporary files - the
    // one from the system may be inaccessible due to sandboxing.
    final cachebase = (await getTemporaryDirectory()).path;
    // We can't access /tmp on Android, which sqlite3 would try by default.
    // Explicitly tell it about the correct temporary directory.
    sqlite3.tempDirectory = cachebase;

    return NativeDatabase.createInBackground(file);
  });
}

3. データベースクラスのインスタンス化

あとはfinal database = AppDatabase();でインスタンス化し、インスタンスに対してクエリを実行して操作

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final database = AppDatabase();

  await database.into(database.todoItems).insert(TodoItemsCompanion.insert(
        title: 'todo: finish drift setup',
        content: 'We can now write queries and define our own tables.',
      ));
  List<TodoItem> allItems = await database.select(database.todoItems).get();

  print('items in database: $allItems');
}
heyhey1028heyhey1028

テーブル定義

基本のデータ型

大体以下で対応できるはず。

Dart type 対応クラス 対応するSQL liteのデータ型
int integer() INTEGER
BigInt int64() INTEGER (useful for large values on the web)
double real() REAL
boolean boolean() INTEGER, which a CHECK to only allow 0 or 1
String text() TEXT
DateTime dateTime() INTEGER (default) or TEXT depending on options
Uint8List blob() BLOB
Enum intEnum() INTEGER (more information available here)
Enum textEnum() TEXT (more information available here)

nullable

nullableのカラムについてはnullable()()でカラムクラスを作成する

class Items extends Table {
  IntColumn get category => integer().nullable()();
  // ...
}

外部キー参照

外部キー参照をするカラムはreference(テーブル名、#フィールド名)でカラムクラスを作成する

class TodoItems extends Table {
  // ...
  IntColumn get category =>
      integer().nullable().references(TodoCategories, #id)();
}

("Category")
class TodoCategories extends Table {
  IntColumn get id => integer().autoIncrement()();
  // and more columns...
}

デフォルト値

カラムにデフォルト値を持たせたい場合は.withDefault(const Constant(デフォルト値))()でカラムクラスを作成

  BoolColumn get enabled => boolean().withDefault(const Constant(false))();

ユニーク値

カラムをユニーク値にしたい場合はunique()()でカラムクラスを作成

  IntColumn get unique => integer().unique()();

プライマリ・キー

heyhey1028heyhey1028

クエリ

Select

  • Databaseクラスに生えているselect()メソッドでテーブルを指定
  • get()メソッドでテーブル内のデータのFutureを取得
Future<List<TodoItem>> get allTodoItems => database.select(todoItems).get();
  • watch()メソッドでテーブル内データのStreamを取得
Stream<List<TodoItem>> watchEntriesInCategory(Category c) {
  return (database.select(todos)..where((t) => t.category.equals(c.id))).watch();
}
  • select()メソッドに続く形でDatabaseクラスに生えているwhere()メソッドでフィルタリング
  • where()メソッド内ではレコードのフィールドに対しequals()not()isNull()などでクエリを掛けてフィルタリングする

OrderBy

Join

https://drift.simonbinder.eu/docs/dart-api/select/

Update / Delete

https://drift.simonbinder.eu/docs/dart-api/writes/#updates-and-deletes

Insert