Software Framework/Flutter
[Flutter] Drift 사용 예제
Bull_
2025. 3. 23. 18:22
SQL 쉽게 핸들가능한 ORM 라이브러리
라이브러리
2025.03.23 기준
dependencies:
drift: ^2.26.0
drift_flutter: ^0.2.4
path_provider: ^2.1.5
path: ^1.9.0
drift_dev: ^2.26.0
build_runner: ^2.4.15
database 정의
database/app_database.dart
class Todos extends Table {
IntColumn get id => integer().autoIncrement()();
TextColumn get title => text().withLength(min: 1, max: 50)();
BoolColumn get completed => boolean().withDefault(Constant(false))();
}| 필드 | 설명 |
|---|---|
| id | 정수형 기본키, 자동 증가 |
| title | 최소 1자, 최대 50자의 텍스트 |
| completed | 완료 여부 (bool), 기본값은 false |
withLength : 길이 설정 |
|
withDefault : 기본값 설정 |
@DriftDatabase(tables: [Todos])
class AppDatabase extends _$AppDatabase {
AppDatabase() : super(_openConnection());
@override
int get schemaVersion => 1;
Future<List<Todo>> getAllTodos() => select(todos).get();
Stream<List<Todo>> watchAllTodos() => select(todos).watch();
Future insertTodo(TodosCompanion todo) => into(todos).insert(todo);
Future updateTodo(Todo todo) => update(todos).replace(todo);
Future deleteTodo(Todo todo) => delete(todos).delete(todo);
}@DriftDatabase(tables: [Todos]) :
- 이 클래스(AppDatabase)는 Drift 데이터베이스이고, 여기에 포함된 테이블은 Todos다.
- 툴/프레임워크/컴파일러가 인식하기 위해 존재
- “이건 특별한 역할이 있어!”
- build_runner가 이 애너테이션을 보고 _$AppDatabase와 관련된 코드를 자동으로 생성
_$AppDatabase :
- drift가 generate 해주는 database 클래스
schemaVersion :
- 스키마 형태가 바뀌면 올려줘야 함.
Future<List<Todo>> getAllTodos() => select(todos).get();
Stream<List<Todo>> watchAllTodos() => select(todos).watch();
Future insertTodo(TodosCompanion todo) => into(todos).insert(todo);
Future updateTodo(Todo todo) => update(todos).replace(todo);
Future deleteTodo(Todo todo) => delete(todos).delete(todo);select :
| 메서드 | 의미 |
|---|---|
| .get() | 한 번 데이터를 가져옴 (한 번만 실행), 한 번만 읽는 경우에만 하면 됌. |
| .watch() | 스트림으로 감시 (데이터가 바뀌면 자동 업데이트됨) |
| .where(...) | 조건절 추가 가능 |
insert :
TodosCompanion: DB에 값 넣을 땐 이거 사용해야 됌
update :
- 업데이트하는데 전체리스트를 불러오고,
todos는 drift에 의해 이미 g파일에 생성되어 있음. 즉 전체테이블리스트.replace를 통해 일치하는 id에서 바꿔짐.
delete :
- 이것도 마찬가지로 id에 의해 삭제.
LazyDatabase _openConnection() {
return LazyDatabase(() async {
final dbFolder = await getApplicationDocumentsDirectory();
final file = File(p.join(dbFolder.path, 'db.sqlite'));
return NativeDatabase(file);
});
}- SQLite 파일을 앱 내에서 저장할 위치를 설정하고, DB 연결을 열어주는 함수
- 중요한 건, 템플릿처럼 Default로 쓰면 됌.
LazyDatabase(...) :
- Drift에서 제공하는 특별한 객체
- DB가 실제로 필요할 때 연결
- 앱 시작할 때 미리 열지 않기 때문에 효율적
- 앱 켜자마자 DB 연결 x → insert, query할 때 연결 o
() async { ... } :
- 데이터베이스 파일은 디스크에 저장되기 때문에, 경로 탐색이 비동기
- 그래서 함수도 async
getApplicationDocumentsDirectory() :
- 이건 앱 전용 저장소 경로를 가져오는 함수
- Android에서는 /data/data/com.yourapp/files/ 같은 위치
- 앱 외부에서 접근 불가한 안전한 저장소
File(p.join(dbFolder.path, 'db.sqlite')) :
- 이 줄에서 SQLite 데이터베이스 파일 경로를 설정
- p.join(...) 은 경로를 OS에 맞게 자동으로 이어주는 역할 (/ 또는 \ 등 처리)
return NativeDatabase(file)
- Drift에게 “여기 이 파일에 DB 저장해줘” 라고 알려줌
- 실제 DB는 이 파일에 쓰이고 읽힘
g파일 생성
flutter pub run build_runner build
사용 방법
- db 인스턴스를 만든다.
final db = AppDatabase(); - 스트림에 watch 등록.
StreamBuilder<List<Todo>>( stream: db.watchAllTodos(), builder: (context, snapshot) { }, ), - snapshot을 통해 해당 시점의 data 확인.
- 사용에 맞게
db.method()로 호출.builder: (context, snapshot) { final todos = snapshot.data ?? []; return ListView.builder( itemCount: todos.length, itemBuilder: (_, index) { final todo = todos[index]; return ListTile( title: Text(todo.title), trailing: Checkbox( value: todo.completed, onChanged: (val) { db.updateTodo(todo.copyWith(completed: val)); }, ), ); }, ); },
main.dart
import 'package:flutter/material.dart';
import 'database/app_database.dart';
import 'package:drift/drift.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final db = AppDatabase();
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Drift Demo',
home: Scaffold(
appBar: AppBar(title: Text('TODO List')),
body: StreamBuilder<List<Todo>>(
stream: db.watchAllTodos(),
builder: (context, snapshot) {
final todos = snapshot.data ?? [];
return ListView.builder(
itemCount: todos.length,
itemBuilder: (_, index) {
final todo = todos[index];
return ListTile(
title: Text(todo.title),
trailing: Checkbox(
value: todo.completed,
onChanged: (val) {
db.updateTodo(todo.copyWith(completed: val));
},
),
);
},
);
},
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.add),
onPressed: () {
db.insertTodo(
TodosCompanion(title: Value('New Todo')),
);
},
),
),
);
}
}