Flutter/Package

[Flutter] Drift

찌김이 2022. 7. 25. 23:08
728x90
반응형

Flutter 에서 로컬 데이터베이스 패키지 중 하나인 Drift 에 대해서 포스팅합니다.

Drift 는 Sqlflite 와 다르게 ORM 방식의 데이터베이스 입니다.

ORM 은 Object Relational Mapping (객체-관계 매핑), 쉽게 말해서 객체와 관계형 데이터베이스의 데이터를 연결해주는 것을 말합니다.

Android 의 Room , Spring 의 JPA, Node.js 의 Sequelize 등을 써보신 분은 익숙할 것입니다.

 

drift | Dart Package

Drift is a reactive library to store relational data in Dart and Flutter applications.

pub.dev

 

pubspec.yaml 

dependencies:
  drift: ^1.7.1
  path_provider: ^2.0.11
  path: ^1.8.2
  
dev_dependencies:
  build_runner: ^2.2.0
  drift_dev: ^1.7.0

 

todos.dart

  • 모델 클래스를 만들고 Table 을 상속해줍니다.
  • 타입Column get 변수명 => 타입()(); 형태로 선언해줍니다.
  • Sqlflite 와 다르게 Boolean 처리를 알아서 해줍니다.
import 'package:drift/drift.dart';

class Todos extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get title => text()();
  BoolColumn get done => boolean()();
}

 

todo_db_helper.dart

  • 로컬 DB 를 사용하게 해주는 Helper 클래스를 구현해줍니다.
import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:flutter_drift/todos.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

part 'todo_db_helper.g.dart';

@DriftDatabase(
  tables: [Todos],
)
class TodoDbHelper extends _$TodoDbHelper {
  TodoDbHelper() : super(_openConnection());
  
  @override
  int get schemaVersion => 1;
  
}

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
  	
    // path_provider 를 통해 앱의 저장위치 얻음 
    final dbFolder = await getApplicationDocumentsDirectory();
    
    // 해당 경로에 파일 생성
    final file = File(p.join(dbFolder.path, 'db.sqlite'));
    return NativeDatabase(file);
  });
}

 

 

  • 터미널에 아래 명령어를 넣어주어 todo_db_helper.g.dart 을 생성합니다.
flutter pub run build_runner build

  • 생성이 완료 되었으면 todo_db_helper.dart 에 CRUD 코드를 넣어줍니다.
import 'dart:io';

import 'package:drift/drift.dart';
import 'package:drift/native.dart';
import 'package:flutter_drift/todos.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';

part 'todo_db_helper.g.dart';

@DriftDatabase(
  tables: [Todos],
)
class TodoDbHelper extends _$TodoDbHelper {
  TodoDbHelper() : super(_openConnection());

  @override
  int get schemaVersion => 1;

  Future<List<Todo>> findAll() =>
      (select(todos)..orderBy([(t) => OrderingTerm.desc(t.id)])).get();

  Future<int> insertTodo(TodosCompanion todo) => into(todos).insert(todo);

  Future<int> updateTodo(Todo todo,) =>
      (update(todos)..where((t) => t.id.equals(todo.id))).write(todo);

  Future<int> deleteTodo(int id) =>
      (delete(todos)..where((t) => t.id.equals(id))).go();
}

LazyDatabase _openConnection() {
  return LazyDatabase(() async {
  	
    // path_provider 를 통해 앱의 저장위치 얻음 
    final dbFolder = await getApplicationDocumentsDirectory();
    
    // 해당 경로에 파일 생성
    final file = File(p.join(dbFolder.path, 'db.sqlite'));
    return NativeDatabase(file);
  });
}

 

todo_screen.dart

  • 예시 화면은 Sqlflite 와 동일하게 간단한 CRUD 만 동작하게 구현했습니다.
  • FutureBuilder 를 이용하여 todo 목록 정보를 가져옵니다.
import 'package:drift/drift.dart';
import 'package:flutter/material.dart';
import 'package:flutter_drift/todo_db_helper.dart';

class DriftScreen extends StatefulWidget {
  const DriftScreen({Key? key}) : super(key: key);

  @override
  State<DriftScreen> createState() => _DriftScreenState();
}

class _DriftScreenState extends State<DriftScreen> {
  TodoDbHelper todoDbHelper = TodoDbHelper();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'Drift Sample Page',
        ),
      ),
      body: FutureBuilder<List<Todo>>(
        future: todoDbHelper.findAll(),
        builder: (context, snapshot) {
          // loading
          if (!snapshot.hasData) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          // empty
          if (snapshot.data!.isEmpty) {
            return const Center(
              child: Text(
                'Todo is empty',
                style: TextStyle(
                  fontSize: 20.0,
                ),
              ),
            );
          }

          return ListView(
            children: snapshot.data!
                .map(
                  (e) => ListTile(
                title: Text('${e.title} ${e.id}'),
                leading: updateBtn(e),
                trailing: deleteBtn(e.id!),
              ),
            )
                .toList(),
          );
        },
      ),
      floatingActionButton: addBtn(),
    );
  }

  Widget deleteBtn(int id) => IconButton(
    onPressed: () {
      setState(() {
        todoDbHelper.deleteTodo(id);
      });
    },
    icon: const Icon(Icons.delete_forever),
  );

  Widget updateBtn(Todo todo) => IconButton(
    onPressed: () {
      setState(() {
        var title =
        todo.title.contains('update') ? 'title' : 'update title';

        todoDbHelper.updateTodo(
          Todo(
            id : todo.id,
            title: title,
            done: false,
          ),
        );
      });
    },
    icon: const Icon(Icons.update_sharp),
  );

  FloatingActionButton addBtn() => FloatingActionButton(
    child: Icon(Icons.add),
    onPressed: () {
      setState(() {
        todoDbHelper.insertTodo(
          TodosCompanion(
            title: Value('title'),
            done: Value(true),
          ),
        );
      });
    },
  );
}

 

 

 

728x90
반응형