旧サイトから記事の移行中です

Driftの導入手順:インストール・テーブル定義・基本的なメソッド作成と実行方法

FlutterのDriftパッケージを使うメリットは、「SQLiteデータベースの作成・データの登録・抽出・更新・削除等のデータベースの操作を、SQL文を直接書かなくても、Dartの文法のみで実行できる」こと。

SQLiteは「SQLite3」というパッケージがありますが、SQLite3を使うには、自分でSQL構文を書く必要があります。

SQL構文に不慣れな人が、視覚的な操作なしにSQLデータベースを作るのは大変です。

でも、DriftパッケージにはSQLite3パッケージとの依存関係があって、SQLiteとDartとの橋渡し役をしてくれます。

この記事では、Driftの導入からメソッドの作成・実行までの基本的な流れを紹介します。

動作確認バージョン
  • Drift: 2.22.0
  • Flutter: 3.24.4
  • Dart: 3.5.4
  • Android Studio: Koala(2024.1.1)
目次

Driftパッケージのインストール

Drift公式サイトの「Getting Started」の手順でインストールする
Driftの公式サイトの「Getting Started」に従ってインストールする

Driftを使うには、Driftパッケージの他に「drift_flutter」と、開発段階で必要なパッケージを2つインストールする必要があります。
Drift単体では機能しません。

Drift以外の複数のパッケージを一つ一つインストールするのは面倒なので、以下のようにpubspec.yamlに一度に全て入力して、pub getすることにします。

pubspec.yamlファイルの、「dependencies」に「drift」と「drift_flutter」、「dev_dependencies」に「drift_dev」と「build_runner」を入れ、「Pub get」して、一度に4つをインストールする。
pubspec.yamlを修正したら「pub get」でインストール完了

build_runnerは、もしすでに「dev_dependencies:」に入れてあるなら、バージョンをDrift指定のバージョンに書き換えればOKです。

古いバージョンのDriftでは、もっとたくさんのパッケージが必要だったけど、現在のDrift2.22.0では、この4つになっています。

でも、今後変わる可能性はあるので、導入時は公式サイトで確認すべし。

Driftの公式サイト

コマンドラインから必要なパッケージを一度にインストールする場合は、ターミナルで以下のコマンドでいけます。

dart pub add drift drift_flutter dev:drift_dev dev:build_runner

詳細は、公式サイト(https://drift.simonbinder.eu/setup/#the-dependencies)の
’Alternatively, you can achieve the same result using the following command:’の項を参照。

pub.devのDriftのInstallingのページでは、「flutter pub add drift」とコマンドを走らせるように出ているけど、この方法でインストールできるのは、Driftだけです。

pub.devのdriftページの「Installing」でインストールできるのはdriftだけ。

コード生成ファイルを定義

Driftを使うための一連の定義は、一つのファイルにまとめておく必要があります。

ここでは、dbフォルダに「database.dart」を作って、ここに定義を書いていきます。

Driftでは、定義ができたら「build_runner」を走らせて、別の定義ファイルを自動作成するのでDrift用のフォルダを作っておくのがオススメ。

Driftの定義ファイルは、データベース用のフォルダに入れる。
定義ファイルはデータベース用のフォルダの中に作成する

定義ファイルには、まず「drift」のimportと「database.g.dart」をpartとして取り込む宣言文を書きます。

「database.g.dart」は、いま自分で作った「database.dart」の拡張子の前に「.g」を入れるのがDriftのルールです。

後で、Dartが「database.g.dart」を自動生成します。

import 'package:drift/drift.dart';
part 'database.g.dart';

この時点ではエラーの赤い線が出てるけど、今はこのままでOK。

テーブルのクラスを作成

次に、DriftのTableをextendsしてデータベースのテーブルを作ります。

DriftのTableをextendsしてデータベースのテーブルを作る。
import 'package:drift/drift.dart';
part 'database.g.dart';

class TodoItems extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get title => text()();
  TextColumn get content => text()();
}

データベース名(ここでは「TodoItems」)は、必ず「複数形」にします。

複数形の単語が「テーブル名」、その単数形が「レコード1行分」というように、Dartが自動で認識します。

テーブルクラスは、ここでは一つだけ作っていますが、複数個作ることができます。

nullableやprimaryKeyの指定、テキストなら最小・最大文字数などもテーブルクラスで指定します。
これらは、このテーブルではやってないけど、integer()には.autoIncrement()()をつけて、暗黙的に主キーとしての設定だけしてみました。

データベースクラスの作成

テーブルの定義ができたら、そのテーブルクラスをもとにしたデータベースのクラスを作成します。

DriftのTableをextendsしたクラスを引数にして、データベースのクラスを作る。
テーブルクラスを複数作る場合は、tables:のリストにカンマ区切りで配列指定する
@DriftDatabase(tables: [TodoItems])
  class MyDatabase extends _$MyDatabase {
}

@DriftDatabaseのtables: 引数には、作成したテーブルクラスを指定します。

複数形が「Categories」のような、単数形の「Category」に単に「s」をつけない単語をテーブルクラス名に使うときは、次のように「@DataClassName(‘Category’);」をつけて定義します。

@DataClassName('Category');
class Categories extends Table {
  hogehoge...
}

「build_runner」で「〜.g.dart」を生成

データベースクラスの大枠が作れたら、ターミナルでbuild_runnerを走らせます。

dart run build_runner build

すると、「database.g.dart」が作成されます。
この「.g.dart」ファイルは、手入力で修正してはいけません。

テーブルクラスの構造を変えたときは、再度build_runnerを走らせます。

buld_runnerで作成された「.g.dart」ファイルは、手作業で修正してはいけない。
「.g.dart」ファイルの先頭には「// GENERATED CODE – DO NOT MODIFY BY HAND」の記述

もし、すでにbuild_runnerを走らせたことがある場合は、その旨のエラーメッセージが出ることがあります。

その対処法として、ターミナルのメッセージに選択肢が示されるので、Deleteを選びます。

  1. Delete
  2. ignore
  3. …..  たしか、こんな感じの選択肢だったと思う…

データベース接続のコンストラクターを作成

.g.dartのコード生成が終わったら、さきほど作った「MyDatabase」クラスにコンストラクタを作ります。

MyDatabaseクラスに作ったコンストラクター

このコンストラクタで、データベースを保存する場所をDriftに伝えます。

このコードはDriftの公式サイトからコピペして、クラス名(MyDatabaseとextendsする_$の後の名前)と、driftDatabaseの引数(my_database)だけ修正すればOK。

@DriftDatabase(tables: [TodoItems])
class MyDatabase extends _$MyDatabase {
  MyDatabase() : super(_openConnection());

  static QueryExecutor _openConnection() {
    return driftDatabase(name: 'my_database');
  }

「driftDatabase」の引数「name: 」は、SQLiteデータベースのファイルとして保存されるファイル名にもなります。

ここでは「my_database」としました。拡張子なしで指定します。

driftDatabase()クラスは「drift_flutterパッケージ」が必要なので、ファイルの冒頭にimport文で追加します。

import 'package:drift_flutter/drift_flutter.dart';

スキーマバージョンの設定

ここまでの段階では、クラス名(MyDatabase)が赤くエラー表示されていると思うけど、このエラーはスキーマバージョンを指定すると解消されます。

スキーマバージョンのエラーメッセージ

スキーマバージョンとは、作成したデータベースのバージョンです。
新規作成時は「1」です。

設定方法は、Drift公式サイトから「schemaVersion」の行をコピペしてもいいけど、クラス名(MyDatabase)から「オプション()+Enter()」で「Create 1 missing override」を選ぶとAndroid Studioが入れてくれます。

DriftDatabaseのクラス名のエラーは、スキーマバージョンを入れると解消する
  @override
  // TODO: implement schemaVersion
  int get schemaVersion => throw UnimplementedError();

上のように自動挿入されたら、「throw UnimplementedError()」の部分を、下のコードのように「1」にすればOK。

  @override
  int get schemaVersion => 1;

スキーマバージョンの数字は必ず1から始めます。
整数しか使えないので、「1.5」とかはダメです。

データベースの登録・抽出・更新・削除メソッドの作成

前項までが終わると、メソッドが作成できるようになります。

メソッドの作成は、プロジェクトやデータベースの規模に応じて構築方法が違ってくると思うけど、ここでは、最も基礎的な方法でデータベースを操作するSQLクエリをメソッドとして作成していきます。

これらのメソッドも、「database.dart」の中に書きます。

MyDatabaseクラスの中に、データの登録・抽出・削除・更新のメソッドを作る

データの登録(Create)INSERT

Driftでデータベース登録するメソッドのコード。

初めてメソッドを作るときは文法がさっぱりわからないので、intoの引数にはとりあえずテーブル名の先頭を小文字に入力したら、その引数上でF4を押すと、「database.g.dart」の定義に飛ぶので、少し眺めるとなんとなくわかる、気がします…気がします…

Future<void> addTodo(TodoItem item) => into(todoItems).insert(item);

データの抽出(Read)SELECT

Future<List<TodoItem>> get allItems => managers.todoItems.get();

ここでは、get(ゲッター)を使っているけど、getを使わなければ下のように書いても同じ。

Future<List<TodoItem>> getAllItems() async {
  return await managers.todoItems.get();
}

データの更新(Update)

下のコードでは、データベースの1行分を丸ごと更新します。

Future<void> updateTodo(TodoItem itemUpdate) =>
    managers.todoItems.replace(itemUpdate);

データの削除(Delete)

対象となる1行を削除します。

コード内の「f」は、「filter」の「f」。
カラム名「id」の値でフィルターして、その値がデータベース内の値と一致したら削除する、という感じ。

Future<void> deleteTodo(TodoItem itemDelete) =>
    managers.todoItems.filter((f) => f.id(itemDelete.id)).delete();

データベースインスタンスの取得

メソッドができたら、いよいよそれを使う準備です。

データベースのインスタンスは、プロジェクトの中で1つだけ持つよう推奨されているので、トップレベルのグローバル変数として「main.dart」で宣言します。

import 'package:flutter/material.dart';
import 'package:drift_try/db/database.dart';

late MyDatabase database;

void main() {
  database = MyDatabase();
  runApp(const MyApp());
}

「database.dart」をインポートして、MyDatabaseのクラス変数を宣言します。
先頭にはlateをつけて、null Safetyに対応させます。

参照したのは、Dart公式FAQの「Using the database」の項

クエリメソッドの実行

メソッドの実行は、「ElevatedButton」の「onPressed:」に入れてみました。

ここでは、main.dartだけで画面を作っています。(main.dartの全文はこの記事の最後のちょっと前に載せました)

// データの登録
ElevatedButton(
  onPressed: () => _databaseIns(), child: const Text('登録')),
// データの削除
ElevatedButton(
  onPressed: () => _databaseDel(), child: const Text('削除')),
// データの更新
ElevatedButton(
  onPressed: () => _databaseUpdate(), child: const Text('30を修正')),
FlutterのWidget(ElevatedButton)を押したら、onPressedメソッドでデータの登録・削除・修正がされるように作成したアプリ画面

データの登録「_databaseIns()」

データベースに3行分を登録するElevatedButtonの処理コード。

_databaseIns() {
  TodoItem item;
  // 10 を登録
  item = const TodoItem(id: 10, title: '1番目', content: '1番目の詳細');
  database.addTodo(item);
  // 20 を登録
  item = const TodoItem(id: 20, title: '2番目', content: '2番目の詳細');
  database.addTodo(item);
  // 30 を登録
  item = const TodoItem(id: 30, title: '3番目', content: '3番目の詳細');
  database.addTodo(item);
    
}

「DB Browser for SQLite」で中身を見てみる

Driftで3行分を登録後に、「DB Browser for SQLite」で中身を見たところ
3行分登録されました

「DB Browser for SQLite」は、SQLiteまたはSQLCipherデータベースファイルを、視覚的に作成・検索・編集できるオープンソースツールです。

「DB Browser for SQLite」公式サイト

「DB Browser for SQLite」の初期画面

「DB Browser for SQLite」でデータベースの中身を見てみるには、メソッドの実行後にsqliteファイルをダウンロードして、「DB Browser for SQLite」で開きます。

Android Studioでsqliteファイルをダウンロードするには、「Device Explorer」から、「_/data/data/(プロジェクト名)/app_flutter/(データベース名).sqlite」を右クリックし、「Save as」です。

※プロジェクト名を確認するには、「(プロジェクトフォルダ)/android/app/build.gradle」というファイルの「android {namespace = “〜〜”」の値。

データの削除「_databaseDel()」

データベースの「id:10」の行を削除するElevatedButtonの処理コード。

_databaseDel() {
  TodoItem item;

  // 10 を削除
  item = const TodoItem(id: 10, title: '1番目', content: '1番目の詳細');
  database.deleteTodo(item);
}

「DB Browser for SQLite」で中身を見てみる

Driftでid:10の行を削除後に、「DB Browser for SQLite」で中身を見たところ
id:10を削除したところ

データの更新「_databaseUpdate()」

データベースの「id:30」の行を修正するElevatedButtonの処理コード。

_databaseUpdate() {
  TodoItem item;

  // 30 を修正
  item = const TodoItem(id: 30, title: '3番目を修正', content: '3番目の詳細を修正');
  database.updateTodo(item);
}

「DB Browser for SQLite」で中身を見てみる

Driftでid:13の行を修正後に、「DB Browser for SQLite」で中身を見たところ
id:30 の内容が修正されている

main.dartの全データ

import 'package:flutter/material.dart';
import 'package:drift_try/db/database.dart';

late MyDatabase database;

void main() {
  database = MyDatabase();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Driftの導入',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Driftの導入'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});
  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // データの登録
            ElevatedButton(
                onPressed: () => _databaseIns(), child: const Text('登録')),
            // データの削除
            ElevatedButton(
                onPressed: () => _databaseDel(), child: const Text('削除')),
            // データの更新
            ElevatedButton(
                onPressed: () => _databaseUpdate(), child: const Text('30を修正')),
          ],
        ),
      ),
    );
  }

  _databaseIns() {
    TodoItem item;
    // 10 を登録
    item = const TodoItem(id: 10, title: '1番目', content: '1番目の詳細');
    database.addTodo(item);
    // 20 を登録
    item = const TodoItem(id: 20, title: '2番目', content: '2番目の詳細');
    database.addTodo(item);
    // 30 を登録
    item = const TodoItem(id: 30, title: '3番目', content: '3番目の詳細');
    database.addTodo(item);
  }

  _databaseDel() {
    TodoItem item;
    // 10 を削除
    item = const TodoItem(id: 10, title: '1番目', content: '1番目の詳細');
    database.deleteTodo(item);
  }

  _databaseUpdate() {
    TodoItem item;
    // 30 を修正
    item = const TodoItem(id: 30, title: '3番目を修正', content: '3番目の詳細を修正');
    database.updateTodo(item);
  }
}

Driftの特徴

Driftは、「sqlite3」「sqflite」などのライブラリ上に構築されたパッケージです。

Driftを使うと、SQL文を書かなくてもSQLiteデータベースを扱えますが、SQL文を直接書いても使えます。

DAOもサポートしているし、Providerでラップすることもできます。

この記事では、まずはDriftの導入とその基本概要をまとめました。

よかったらシェアしてね!
  • URLをコピーしました!
目次