관리 메뉴

Bull

[Flutter] Drift 사용 예제 본문

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

사용 방법


  1. db 인스턴스를 만든다.
    final db = AppDatabase();
  2. 스트림에 watch 등록.
    StreamBuilder<List<Todo>>(  
    stream: db.watchAllTodos(),  
    builder: (context, snapshot) {  
    },  
    ),
  3. snapshot을 통해 해당 시점의 data 확인.
  4. 사용에 맞게 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')),  
            );  
          },  
        ),  
      ),  
    );  
  }  
}