FlutterでDBを扱う方法はいくつかありますが、代表的な方法の一つがSQLiteです。ネットワーク通信が必要なアプリでFirebaseなどのバックエンドサービスを導入するのであればそちらのDBを使うのが良いと思いますが、ローカルで完結するアプリであればSQLiteの利用がおすすめです。
そこで、今回はFlutterでローカルにSQLiteのDBを構築し、基本的な操作を行う方法について説明します。
SQLiteのインストール
FlutterでSQLiteを扱うためのパッケージでsqfliteがあります。以下のようにpubspec.yaml
ファイル依存関係を追加します。アプリの初期起動を判定するのにshared_preferencesライブラリを使うので同時にインストールを行います。
dependencies:
sqflite: ^2.2.5
shared_preferences: ^2.0.18
flutter pub getを実行して、依存関係を解決します。
データベースの作成
sqfliteパッケージを使って、データベースを作成します。データベースの作成は、main関数でアプリケーションの初回起動時の一度だけ実行する方法があります。SharedPreferencesライブラリを使って、初回起動フラグを管理することで、初回起動時のみデータベースの作成処理を実行することができます。
以下、データベース作成のサンプルコードです。
import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:sqflite/sqflite.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// SharedPreferencesオブジェクトを取得
final SharedPreferences prefs = await SharedPreferences.getInstance();
// 初回起動かどうかを判定
bool isDbCreated = prefs.getBool('isDbCreated') ?? false;
if (!isDbCreated) {
// DBを作成
final Future<Database> database = openDatabase(
join(await getDatabasesPath(), 'my_database.db'),
onCreate: (db, version) {
return db.execute(
'CREATE TABLE my_table(id INTEGER PRIMARY KEY, name TEXT)',
);
},
version: 1,
);
// DBが完全に作成されるまで待機
await database;
// 初回起動フラグを更新
prefs.setBool('isDbCreated', true);
}
runApp(const MainApp());
}
DB作成について詳しく解説します。openDatabase
関数を使用して、データベースを開いています。この関数はデータベースがまだ作成されていない場合、DBを構築しonCreate
コールバック関数を呼び出すので、そこでtableの作成を行なっています。
データベースのパスは join(await getDatabasesPath(), ‘my_database.db’) の部分です。getDatabasesPath()
関数はアプリのデータベースファイルが保存されるディレクトリのパスを取得します。
最後に、await database
を使用して、openDatabase
関数が完了するのを待ちます。
FlutterでCRUD作成
以下はデータベースの基本操作を実装したサンプルコードです。参考程度に、DBから取得したデータをリストで表示する部分も実装しています。
import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
class MyTablePage extends StatefulWidget {
const MyTablePage({Key? key}) : super(key: key);
@override
MyTablePageState createState() => MyTablePageState();
}
class MyTablePageState extends State<MyTablePage> {
late final Future<List<Map<String, dynamic>>> _items;
@override
void initState() {
super.initState();
_items = read('test');
}
Future<Database> getDB() async{
return openDatabase(
join(await getDatabasesPath(), 'my_database.db'),
);
}
Future<int> create(String name) async{
// Create
final Database db = await getDB();
int id = await db.insert(
'my_table',
{'name': name}
);
return id;
}
Future<List<Map<String, dynamic>>> read(String name) async{
final Database db = await getDB();
// DBからデータを取得
final List<Map<String, dynamic>> maps = await db.query(
'my_table',
where: 'name = ?',
whereArgs: [name],
orderBy: 'id DESC', // ASC: 昇順, DESC: 降順
);
return maps;
}
Future<int> update(int id, String name) async {
// Update
final Database db = await getDB();
final Map<String, dynamic> data = {
'id': id,
'name': name
};
return await db.update('my_table', data, where: 'id = ?', whereArgs: [id]);
}
Future<int> delete(int id) async {
// Delete
final Database db = await getDB();
return await db.delete('my_table', where: 'id = ?', whereArgs: [id]);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<List<Map<String, dynamic>>>(
future: _items,
builder: (context, snapshot) {
if (snapshot.hasData) {
final items = snapshot.data!;
return ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
final item = items[index];
return ListTile(
title: Text(item['id'].toString()),
subtitle: Text(item['name'] as String),
onTap: () {
print('${item['name']} was tapped!');
},
);
},
);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
} else {
return const Center(
child: CircularProgressIndicator(),
);
}
},
);
}
}
Create: レコードの作成には、insertメソッドを使用します。最初の引数はテーブル名、2番目の引数は挿入するデータのMapです。挿入が成功した場合、メソッドは挿入したレコードのIDを返します。
Read: query
メソッドでデータベースからデータを取得できます。第一引数としてテーブル名をとり、条件はwhereパラメータで指定し、whereに渡すパラメータはwhereArgsに記述します。
update: updateメソッドでレコードを更新します。最初の引数はテーブル名、2番目の引数は挿入するデータのMapです。 query
メソッドと同様、条件はwhereパラメータで指定し、whereに渡すパラメータはwhereArgsに記述します。
delete: deleteメソッドでレコードを削除します。最初の引数はテーブル名です。query
メソッドと同様、条件はwhereパラメータで指定し、whereに渡すパラメータはwhereArgsに記述します。
今回、insertやupdateの際にmapを関数内に作成して使いました。慣れてきたら、my_table
テーブルのレコードを扱うためのクラスを定義することで、コードの再利用性やメンテナンス性が向上するのでチェレンジしてみてください。