こんにちは、ゲームソリューション部のsoraです。
今回は、Flutterで軽量なRDBMSのSQLite(sqflite)を使ってみたことについて書いていきます。
実装した画面
ローカルのデータベースに対する各操作を実行するシンプルなアプリです。
Insert
id・name・messageを入力して、データを挿入します。
Update
idをキーとして、name・messageを更新します。
Delete
idをキーとして、データを削除します。
利用する主要なパッケージ
sqflite
sqflite | Flutter package
使用したバージョン(pubspec.yamlから抜粋):sqflite: ^2.3.2
軽量なリレーショナルデータベースマネジメントシステムであるSQLiteを使うためのパッケージ
flutter_riverpod
flutter_riverpod | Flutter package
使用したバージョン(pubspec.yamlから抜粋):flutter_riverpod: ^2.5.1
状態管理パッケージ
多くの情報が落ちている人気なパッケージのため、詳しい説明は割愛します。
コードの解説
コードは以下です。
main.dart
ではメインの処理、database_connection
ではDB操作、chat_message.dart
ではテーブル内のデータ表示の状態管理をしています。
main.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'chat_message.dart';
import 'database_connection.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(
const ProviderScope(
child: MyApp()
),
);
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'sqflite Test',
theme: ThemeData(),
home: const ChatPage(),
);
}
}
class ChatPage extends ConsumerWidget {
const ChatPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
// DB内のデータの表示用
final chatMessageProviderValue = ref.watch(chatMessageProvider);
// DB操作用
final dataBaseConnectionProviderNotifier = ref.watch(dataBaseConnectionProvider.notifier);
final _idEditController = TextEditingController();
final _nameEditController = TextEditingController();
final _messageEditController = TextEditingController();
// DBの項目、idが主キー
String id;
String name;
String message;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: const Text('sqflite test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
// 入力フィールド
// id
Container(
padding: const EdgeInsets.only(
left: 25,
right: 25,
),
child: TextField(
keyboardType: TextInputType.text,
controller: _idEditController,
decoration: const InputDecoration(
labelText: 'id',
)
)
),
const SizedBox(height: 10),
// name
Container(
padding: const EdgeInsets.only(
left: 25,
right: 25,
),
child: TextField(
keyboardType: TextInputType.text,
controller: _nameEditController,
decoration: const InputDecoration(
labelText: 'name',
)
)
),
const SizedBox(height: 10),
// message
Container(
padding: const EdgeInsets.only(
left: 25,
right: 25,
),
child: TextField(
keyboardType: TextInputType.text,
controller: _messageEditController,
decoration: const InputDecoration(
labelText: 'message',
)
)
),
const SizedBox(height: 10),
// ボタン
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () async{
id = _idEditController.text;
name = _nameEditController.text;
message = _messageEditController.text;
dataBaseConnectionProviderNotifier.dbInsert(id, name, message);
},
child: const Text('insert'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async{
id = _idEditController.text;
name = _nameEditController.text;
message = _messageEditController.text;
dataBaseConnectionProviderNotifier.dbUpdate(id, name, message);
},
child: const Text('update'),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: () async{
id = _idEditController.text;
dataBaseConnectionProviderNotifier.dbDelete(id);
},
child: const Text('delete')
),
],
),
const SizedBox(height: 10),
for(var map in chatMessageProviderValue) ...{
Text('id = ${map["id"]}, name = ${map["name"]}, message = ${map["message"]}'),
}
],
),
),
);
}
}
database_connection.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:sqflite/sqflite.dart';
import 'chat_message.dart';
class DataBaseConnection extends AsyncNotifier<List<Map>> {
late String path;
late Database database;
List<Map> listMap = [];
@override
Future<List<Map>> build() async {
// getDatabasesPath():デフォルトのデータベース保存用フォルダのパスを取得
var databasesPath = await getDatabasesPath();
// 取得したパスから本アプリ用にて生成するDB名を指定
path = '$databasesPath/test.db';
// データベースを開く(pathに存在しなければ新規作成)
database = await openDatabase(
path,
version: 1,
// DBがpathに存在しなかった場合にonCreateが呼び出される
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE Test (id TEXT PRIMARY KEY, name TEXT, message TEXT)'
);
});
return [];
}
void dbInsert(String id, String name, String message) async {
final chatMessage = ref.watch(chatMessageProvider.notifier);
// INSERT
await database.insert(
'Test',
{'id': id, 'name': name, 'message': message},
);
// SELECT
listMap = await database.rawQuery('SELECT * FROM Test');
// 状態更新
chatMessage.update(listMap);
}
void dbUpdate(String id, String name, String message) async {
final chatMessage = ref.watch(chatMessageProvider.notifier);
// idをキーとしてUPDATE
await database.update(
'Test',
{'name': name, 'message': message},
where: 'id = ?',
whereArgs: [id]
);
// SELECT
listMap = await database.rawQuery('SELECT * FROM Test');
// 状態更新
chatMessage.update(listMap);
}
void dbDelete(String id) async {
final chatMessage = ref.watch(chatMessageProvider.notifier);
// idをキーとしてDELETE
await database.delete(
'Test',
where: 'id = ?',
whereArgs: [id]
);
// SELECT
listMap = await database.rawQuery('SELECT * FROM Test');
// 状態更新
chatMessage.update(listMap);
}
}
final dataBaseConnectionProvider = AsyncNotifierProvider<DataBaseConnection, List<Map>>(() {
return DataBaseConnection();
});
chat_message.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ChatMessage extends Notifier<List<Map>> {
@override
List<Map> build(){
return [];
}
void update(List<Map> chatList) {
state = chatList;
}
}
final chatMessageProvider = NotifierProvider<ChatMessage, List<Map>>((){
return ChatMessage();
});
DBの初期化
まずデフォルトのデータベース保存用のフォルダパスを取得して、本アプリに使用するDB名を指定します。
指定したパスにDBが存在しなければ、DBを作成します。
コメント記載の通り、DBがパスに存在しない場合に、onCreateが実行されます。
database_connection.dart
// getDatabasesPath():デフォルトのデータベース保存用フォルダのパスを取得
var databasesPath = await getDatabasesPath();
// 取得したパスから本アプリ用にて生成するDB名を指定
path = '$databasesPath/test.db';
// データベースを開く(pathに存在しなければ新規作成)
database = await openDatabase(
path,
version: 1,
// DBがpathに存在しなかった場合にonCreateが呼び出される
onCreate: (Database db, int version) async {
await db.execute(
'CREATE TABLE Test (id TEXT PRIMARY KEY, name TEXT, message TEXT)'
);
});
DB操作
次にDB操作の部分では、INSERT・UPDATE・DELETEをそれぞれメソッドとして書いています。
database.insert()
・database.update()
・database.delete()
などが用意されています。
database.rawQuery()
でSQL文を書いて指定することが可能です。
DB操作を行った後に、テーブル内の全データ取得をして、状態を更新しています。
database_connection.dart
void dbInsert(String id, String name, String message) async {
final chatMessage = ref.watch(chatMessageProvider.notifier);
// INSERT
await database.insert(
'Test',
{'id': id, 'name': name, 'message': message},
);
// SELECT
listMap = await database.rawQuery('SELECT * FROM Test');
// 状態更新
chatMessage.update(listMap);
}
void dbUpdate(String id, String name, String message) async {
final chatMessage = ref.watch(chatMessageProvider.notifier);
// idをキーとしてUPDATE
await database.update(
'Test',
{'name': name, 'message': message},
where: 'id = ?',
whereArgs: [id]
);
// SELECT
listMap = await database.rawQuery('SELECT * FROM Test');
// 状態更新
chatMessage.update(listMap);
}
void dbDelete(String id) async {
final chatMessage = ref.watch(chatMessageProvider.notifier);
// idをキーとしてDELETE
await database.delete(
'Test',
where: 'id = ?',
whereArgs: [id]
);
// SELECT
listMap = await database.rawQuery('SELECT * FROM Test');
// 状態更新
chatMessage.update(listMap);
}
参考にした記事
【Flutter】ローカルデータベース(sqflite)
SQLiteでのデータ永続化
最後に
今回は、Flutterで軽量なRDBMSのSQLite(sqflite)を使ってみたことを記事にしました。
どなたかの参考になると幸いです。