개인 프로젝트 중 Sqflite를 사용을 위해 pub.dev의 sqflite 공식 example을 참고하였는데, 필요 없는 내용이 너무 많아 한 눈에 보기가 힘들었고, 실질적으로 Sqflite를 연동하는 부분을 찾기가 힘들었다.
Sqlite를 사용해 본 적이 있고, 빠르게 Sqflite를 프로젝트에 적용하기를 원하는 사람들을 위해 정말 빠르고 간단하게 Sqflite로 CRUD를 구현하는 방법을 소개하겠다.

우선 sqflite와 path 패키지를 추가해준다.
- sqflite: SQLite 데이터 베이스를 사용하기 위한 패키지
- path: 파일 및 디렉토리 경로를 조작하기 위한 유틸리티 패키지
패키지 추가 이후에는 사용할 데이터의 클래스를 정의해 준다.
class Script {
final int id;
final String title;
final String content;
const Script({required this.id, required this.title, required this.content});
// #1
factory Script.fromJson(Map<String, dynamic> json) =>
Script(id: json['id'], title: json['title'], content: json['content']);
// #2
Map<String, dynamic> toJson() =>
{'id': id, 'title': title, 'content': content};
}
#1은 팩토리 메서드를 사용하여 JSON형식의 데이터를 받아 Script 클래스의 인스턴스를 생성하는 역할을 한다.
즉, JSON => 객체
#2는 반대로 Script 객체를 JSON형식으로 변환한다.
Class를 생성하였다면 이제 sqflite데이터베이스를 생성하고 사용해보자.
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import '../data/script_data.dart';
class SqliteService {
static late Database _database;
Future<Database> get database async {
if (_database != null) {
return _database;
}
_database = await initializeDB();
return _database;
}
Future<Database> initializeDB() async {
return await openDatabase(
join(await getDatabasesPath(), 'database.db'),
onCreate: (database, version) async {
await database.execute(
"CREATE TABLE Script(id INTEGER PRIMARY KEY AUTOINCREMENT, title NOT NULL, content NOT NULL)",
);
},
version: 1,
);
}
Future<int> addScript(Script script) async {
final db = await database;
return await db.insert("Script", script.toJson(),
conflictAlgorithm: ConflictAlgorithm.replace);
}
Future<List<Script>?> getAllScript() async {
final db = await database;
final List<Map<String, dynamic>> mapData = await db.query("table");
if (mapData.isEmpty) return null;
return List.generate(
mapData.length, (index) => Script.fromJson(mapData[index]));
}
Future<int> updateScript(Script script) async {
final db = await database;
return await db.update(
"Script",
script.toJson(),
where: 'id = ?',
whereArgs: [script.id],
);
}
Future<int> deleteScript(Script script) async {
final db = await database;
return await db.delete("Script", where: "id= = ?", whereArgs: [script.id]);
}
}
기본적으로 클래스는 싱글톤 패턴을 사용하여 구현한다. 앱 전체에서 오직 하나의 Database인스턴스만을 사용하기 위함이다.
만약 싱글톤 패턴에 대한 이해가 없다면
import 'dart:async';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
import '../data/script_data.dart';
class SqliteService {
static late Database _database;
Future<Database> get database async {
if (_database != null) {
return _database;
}
_database = await initializeDB();
return _database;
}
Future<Database> initializeDB() async {
return await openDatabase(
join(await getDatabasesPath(), 'database.db'),
onCreate: (database, version) async {
await database.execute(
"CREATE TABLE Script(id INTEGER PRIMARY KEY AUTOINCREMENT, title NOT NULL, content NOT NULL)",
);
},
version: 1,
);
}
}
이 부분이 Database를 생성하는 부분이라고만 이해하고 있어도 사용에 무방하다. 'database.db'가 데이터를 저장할 db파일의 이름이고, `CREATE ~ NOT NULL`이 테이블의 스키마를 정의하는 쿼리이다.
이 정도의 코드만 작성하여도, 다른 클래스 혹은 위젯에서 SqliteService의 인스턴스를 호출하여 SqliteService클래스 내부의 함수들을 사용할 수 있게 된다.(addScript, updateScript 등)
getAllScript를 예로 들어보면
class DetailScreenContent extends StatefulWidget {
const DetailScreenContent({
super.key,
});
@override
State<DetailScreenContent> createState() => _DetailScreenContentState();
}
class _DetailScreenContentState extends State<DetailScreenContent> {
SqliteService sqliteService = SqliteService();
@override
Widget build(BuildContext context) {
return SliverFillRemaining(
child: Container(
padding: EdgeInsets.all(defaultPadding),
child: Column(
children: [
// ... 중략 ...
FutureBuilder(
future: sqliteService.getAllScript(),
builder: (context, snapshot) {
return ListView.builder(
itemBuilder: (context, index) {
return Text(snapshot.data![index].title);
},
);
},
)
],
),
),
);
}
}
이런 형태로 sqflite에 저장된 데이터를 읽어 올 수 있다. 이 형태를 기본 템플릿으로 하여 좀 더 복잡한 로직의 구성이 가능하다.
끝
'Flutter' 카테고리의 다른 글
[Flutter] JSON 다루기 (리스트, 중첩) (0) | 2024.07.27 |
---|---|
[Flutter] Animation을 사용하여 ListView 선택 효과 만들기(AnimatedPositioned) (0) | 2024.07.21 |
[Flutter] ExpansionTile padding,border 다루기 (1) | 2024.02.13 |
[Flutter] 그림을 그려보자! CustomPaint (1) | 2024.02.01 |
[Flutter] A pragmatic guide to BuildContext in Flutter(요약,번역) (1) | 2024.01.27 |