driftパッケージは、SQLiteの上に構築されており、型安全で、より高レベルのAPIを提供します。
Moor(drift)パッケージの特徴:
メリット:
(1) 型安全性:
- Dart言語の型システムと統合されており、型安全なクエリが書けます。これにより、コンパイル時に型チェックが行われ、ランタイムエラーを減らすことができます。
(2) 高レベルの抽象化:
- SQLクエリをDartコードとして書けるため、クエリの作成が直感的で簡単です。特に複雑なクエリやリレーションを管理する場合に有用です。
(3) 自動コード生成:
build_runner
を使用して、自動的にデータベース関連のコードを生成します。これにより、手動でコードを書く手間を減らし、ミスを減らすことができます。
(4) リレーションの管理:
- リレーショナルデータベースの特性を活かし、複雑なリレーションやクエリを簡単に管理できます。
デメリット:
(1) パフォーマンスのオーバーヘッド:
- 高レベルの抽象化に伴うオーバーヘッドがあり、パフォーマンスが若干低下することがあります。
(2) 依存関係の増加:
moor
(drift) を使用するためには、build_runner
や他の依存関係が必要となり、プロジェクトの複雑さが増します。
(3) 学習曲線:
- 高度な機能を持っているため、学習曲線がやや急です。特に、FlutterやDartに不慣れな初心者には難しい場合があります。
上記の様に、(前回記事に掲載した)sqfliteパッケージがSQLiteを操作する場合に、型を意識しない(つまり、いわゆる文字列として扱う)のに対して、Moor(drift)では(Dart言語の型システムを利用して)方安全なクエリを書くことができる、というのが、大きな違いになっています。
どちらのパッケージを使用するかは、プロジェクトの要件や開発者のスキルセットに依存します。
シンプルで直接的なデータベース操作が必要な場合はsqflite
、型安全性と高レベルの抽象化が重要な場合はMoor (drift)
を選択するのが良いでしょう。
以下、Moor(drift)の利用手順です。
Moor(drift)の利用手順:
1.Flutterプロジェクト作成
(1) ターミナルを開き、以下のコマンドを実行して新しいプロジェクトを作成します。
flutter create drift_example
(2) プロジェクトフォルダに移動します。
cd drift_example
2.必要なパッケージの追加
Driftを使用するために必要なパッケージ(次の4つ)を追加します。
drift と path_provider と sqlite3_flutter_libs と path は、アプリ実行時に使用されます。
https://pub.dev/packages/drift/install
故に、pubspec.yaml では、dependencies 句に登録します。
しかし、drift_dev と build_runner は、開発段階で使用します。
故に、pubspec.yaml では、dev_dependencies 句に登録します。
https://pub.dev/packages/path_provider/install
https://pub.dev/packages/sqlite3_flutter_libs/install
https://pub.dev/packages/path/install
https://pub.dev/packages/drift_dev/install
https://pub.dev/packages/build_runner/install
2.1 アプリ実行時に必要な(drift、path_provider、sqlite3_flutter_libs、path)パッケージの追加
(1) プロジェクトフォルダで次のコマンドによりdrift、path_provider、sqlite3_flutter_libs、path の各パッケージ(の依存関係)を追加します。
flutter pub add drift
flutter pub add path_provider
flutter pub add sqlite3_flutter_libs
flutter pub add puth
(2) 次のコマンドで上記追加を反映します。
flutter pub get
(3) pubspec.yaml
ファイルを開き、上記パッケージ(の依存関係)が追加されていることを確認します。
dependencies:
flutter:
sdk: flutter
drift: ^2.18.0
path_provider: ^2.1.3
sqlite3_flutter_libs: ^0.5.23
path: ^1.9.0
2.2 開発時に必要な(drift_dev、build_runner)パッケージの追加
(1) pubspec.yaml
ファイルを開き、上記パッケージ(の依存関係)を追加します。
dev_dependencies:
flutter_test:
sdk: flutter
drift_dev: ^2.18.0
build_runner: ^2.4.11
(2) ターミナルで以下のコマンドを実行してパッケージをインストール(反映)します。
flutter pub get
3.Driftデータベースの設定と使用
以下は、Driftデータベースを設定し、使用するためのサンプルコードです。
3.1 テーブルとデータベースの設定
まず、テーブルとデータベースを定義します。
(1) lib
フォルダ内に database.dart
ファイルを作成します。
(2) database.dart の内容:
import 'dart:io';
import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
part 'database.g.dart'; // 自動生成されるファイルを指定
// テーブル定義
class Items extends Table {
IntColumn get id => integer().autoIncrement()(); // 自動増分するプライマリキー
TextColumn get name => text()(); // アイテムの名前
}
// データベースクラスの定義
@DriftDatabase(tables: [Items])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1; // スキーマバージョンを設定
// アイテムを追加
Future<int> insertItem(ItemsCompanion item) => into(items).insert(item);
// アイテムを取得
Future<List<Item>> getAllItems() => select(items).get();
// アイテムを削除
Future<int> deleteItem(int id) => (delete(items)..where((tbl) => tbl.id.equals(id))).go();
}
// データベース接続を作成
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase(file);
});
}
3.2 TypeAdapter生成
次に、Driftのコード生成機能を使用して必要なコードを生成します。
(1) ターミナルで以下のコマンドを実行してTypeAdapterを生成します。
flutter pub run build_runner build
3.3 サンプルアプリの作成
次に、サンプルアプリを作成してDriftデータベースを利用します。
(1) lib/main.dart
ファイルを開き、以下のコードを追加します。
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'database.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized(); // Flutterバインディングの初期化
runApp(const MyApp());
}
// メインアプリケーションクラス
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Drift Example',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
// ホーム画面クラス
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
MyHomePageState createState() => MyHomePageState();
}
// ホーム画面の状態を管理するクラス
class MyHomePageState extends State<MyHomePage> {
final AppDatabase _database = AppDatabase(); // データベースのインスタンス
final TextEditingController _controller = TextEditingController(); // テキストフィールドのコントローラ
List<Item> _items = []; // 取得したアイテムを格納するリスト
@override
void initState() {
super.initState();
_loadItems(); // アイテムをロード
}
// データベースからアイテムを読み込むメソッド
Future<void> _loadItems() async {
final items = await _database.getAllItems();
setState(() {
_items = items;
});
}
// アイテムを追加するメソッド
Future<void> _addItem(String name) async {
final newItem = ItemsCompanion(
name: Value(name),
); // 新しいアイテムを作成
await _database.insertItem(newItem); // アイテムをデータベースに追加
_controller.clear(); // テキストフィールドをクリア
_loadItems(); // アイテムを再読み込み
}
// アイテムを削除するメソッド
Future<void> _deleteItem(int id) async {
await _database.deleteItem(id); // 指定したIDのアイテムを削除
_loadItems(); // アイテムを再読み込み
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Drift Example'),
),
body: ListView(
padding: const EdgeInsets.all(8.0),
children: [
TextField(
controller: _controller,
decoration: InputDecoration(
labelText: 'Item Name',
suffixIcon: IconButton(
icon: const Icon(Icons.add),
onPressed: () {
if (_controller.text.isNotEmpty) {
_addItem(_controller.text); // アイテムを追加
}
},
),
),
),
const SizedBox(height: 10),
..._items.map((item) => ListTile(
title: Text(item.name), // アイテムの名前を表示
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
_deleteItem(item.id); // アイテムを削除
},
),
)),
],
),
);
}
}
3.4 動作確認
下の様に、Item Name(TextField)に入力し、+アイコン(IconButton)をクリックすると、その都度、入力データがSQLiteに登録され、その下の表示欄(ListTile)に表示されていきます。
4.まとめ
drift の利用手順は、
①パッケージ追加(アプリ実行時に必要な4つ(drift、path_provider、sqlite3_flutter_libs、path))
②パッケージ追加(開発時に必要な2つ(drift_dev、build_runner))
③Hiveで使用するデータのモデルクラス作成(例:database.dart)
④コード生成機能によるTypeAdapter生成(例:database.g.dart)
⑤サンプルコード作成(例:main.dart)
という順番で行います。
上記②~④を注意すれば、極めて容易に、DB操作を実現できます。