반응형

SQLite를 사용하여 To-Do 데이터를 저장하는 방법을 설명하겠습니다. Flutter 프로젝트에서 SQLite를 사용하기 위해 sqflite 패키지를 사용합니다. 이 패키지를 사용하여 데이터베이스를 생성하고, 할 일 데이터를 저장하고, 읽고, 업데이트하고, 삭제할 수 있습니다.

 

프로젝트 설정

 

1. pubspec.yaml 파일에 sqflitepath 패키지를 추가합니다.

 

dependencies:
  flutter:
    sdk: flutter
  sqflite: ^2.0.0+4
  path: ^1.8.0

 

디렉토리 및 파일 구조

lib/
  main.dart
  models/
    todo.dart
  screens/
    todo_list_screen.dart
    edit_todo_screen.dart
  services/
    database_helper.dart
  providers/
    todo_provider.dart

 

1. 모델 정의

 

models/todo.dartㅣㅇve

class Todo {
  String id;
  String title;
  bool isDone;

  Todo({
    required this.id,
    required this.title,
    this.isDone = false,
  });

  // 데이터베이스에서 읽은 데이터를 객체로 변환
  factory Todo.fromMap(Map<String, dynamic> json) => new Todo(
    id: json["id"],
    title: json["title"],
    isDone: json["isDone"] == 1,
  );

  // 객체 데이터를 데이터베이스에 저장할 수 있는 형태로 변환
  Map<String, dynamic> toMap() => {
    "id": id,
    "title": title,
    "isDone": isDone ? 1 : 0,
  };
  
}

2. 데이터베이스 헬퍼 클래스 작성

 

services/database_helper.dart

import 'dart:async';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';
import '../models/todo.dart';

class DatabaseHelper {
  static final DatabaseHelper _instance = DatabaseHelper._internal();
  factory DatabaseHelper() => _instance;

  static Database? _database;

  DatabaseHelper._internal();

  Future<Database> get database async {
    if (_database != null) return _database!;
    _database = await _initDatabase();
    return _database!;
  }

  Future<Database> _initDatabase() async {
    String path = join(await getDatabasesPath(), 'todo_database.db');
    return await openDatabase(
      path,
      version: 1,
      onCreate: _onCreate,
    );
  }

  Future _onCreate(Database db, int version) async {
    await db.execute('''
      CREATE TABLE todos(
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        title TEXT,
        isDone INTEGER
      )
    ''');
  }

  Future<List<Todo>> getTodos() async {
    final db = await database;
    var res = await db.query('todos');
    List<Todo> list =
        res.isNotEmpty ? res.map((c) => Todo.fromMap(c)).toList() : [];
    return list;
  }

  Future<int> insertTodo(Todo todo) async {
    final db = await database;
    return await db.insert('todos', todo.toMap());
  }

  Future<int> updateTodo(Todo todo) async {
    final db = await database;
    return await db.update(
      'todos',
      todo.toMap(),
      where: 'id = ?',
      whereArgs: [todo.id],
    );
  }

  Future<int> deleteTodo(int id) async {
    final db = await database;
    return await db.delete(
      'todos',
      where: 'id = ?',
      whereArgs: [id],
    );
  }
}

3. 프로바이더 설정

 

providers/todo_provider.dart

import 'package:flutter/material.dart';
import '../models/todo.dart';
import '../services/database_helper.dart';

class TodoProvider with ChangeNotifier {
  List<Todo> _todos = [];

  List<Todo> get todos => _todos;

  Future<void> loadTodos() async {
    _todos = await DatabaseHelper().getTodos();
    notifyListeners();
  }

  Future<void> addTodo(Todo todo) async {
    await DatabaseHelper().insertTodo(todo);
    await loadTodos();
  }

  Future<void> updateTodo(Todo todo) async {
    await DatabaseHelper().updateTodo(todo);
    await loadTodos();
  }

  Future<void> removeTodo(int id) async {
    await DatabaseHelper().deleteTodo(id);
    await loadTodos();
  }
}

4. UI 작성

 

screens/todo_list_screen.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/todo.dart';
import '../providers/todo_provider.dart';
import 'edit_todo_screen.dart';

class TodoListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('To-Do List'),
      ),
      body: FutureBuilder(
        future: Provider.of<TodoProvider>(context, listen: false).loadTodos(),
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            return Center(child: CircularProgressIndicator());
          }
          return Consumer<TodoProvider>(
            builder: (context, todoProvider, child) {
              return ListView.builder(
                itemCount: todoProvider.todos.length,
                itemBuilder: (ctx, i) => ListTile(
                  title: Text(todoProvider.todos[i].title),
                  trailing: Checkbox(
                    value: todoProvider.todos[i].isDone,
                    onChanged: (_) {
                      todoProvider.updateTodo(
                        Todo(
                          id: todoProvider.todos[i].id,
                          title: todoProvider.todos[i].title,
                          isDone: !todoProvider.todos[i].isDone,
                        ),
                      );
                    },
                  ),
                  onLongPress: () {
                    todoProvider.removeTodo(todoProvider.todos[i].id!);
                  },
                  onTap: () {
                    Navigator.of(context).push(
                      MaterialPageRoute(
                        builder: (context) => EditTodoScreen(todo: todoProvider.todos[i]),
                      ),
                    );
                  },
                ),
              );
            },
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          showDialog(
            context: context,
            builder: (ctx) {
              TextEditingController controller = TextEditingController();
              return AlertDialog(
                title: Text('Add Todo'),
                content: TextField(
                  controller: controller,
                  decoration: InputDecoration(labelText: 'Title'),
                ),
                actions: [
                  TextButton(
                    onPressed: () {
                      if (controller.text.isNotEmpty) {
                        final newTodo = Todo(
                          title: controller.text,
                        );
                        Provider.of<TodoProvider>(context, listen: false).addTodo(newTodo);
                        Navigator.of(ctx).pop();
                      }
                    },
                    child: Text('Add'),
                  ),
                ],
              );
            },
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

screens/edit_todo_screen.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../models/todo.dart';
import '../providers/todo_provider.dart';

class EditTodoScreen extends StatelessWidget {
  final Todo todo;

  EditTodoScreen({required this.todo});

  @override
  Widget build(BuildContext context) {
    TextEditingController titleController = TextEditingController(text: todo.title);

    return Scaffold(
      appBar: AppBar(
        title: Text('Edit Todo'),
      ),
      body: Padding(
        padding: EdgeInsets.all(16.0),
        child: Column(
          children: [
            TextField(
              controller: titleController,
              decoration: InputDecoration(labelText: 'Title'),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () {
                if (titleController.text.isNotEmpty) {
                  final updatedTodo = Todo(
                    id: todo.id,
                    title: titleController.text,
                    isDone: todo.isDone,
                  );
                  Provider.of<TodoProvider>(context, listen: false).updateTodo(updatedTodo);
                  Navigator.of(context).pop();
                }
              },
              child: Text('Update'),
            ),
          ],
        ),
      ),
    );
  }
}

5. 메인 파일 설정

 

main.dart

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/todo_list_screen.dart';
import 'providers/todo_provider.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (_) => TodoProvider()),
      ],
      child: MaterialApp(
        title: 'To-Do App',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: TodoListScreen(),
      ),
    );
  }
}

요약

 

1. `sqflite`와 `path` 패키지를 사용하여 SQLite 데이터베이스를 설정합니다.

2. 데이터 모델(`Todo`)을 정의합니다.

3. 데이터베이스 헬퍼 클래스(`DatabaseHelper`)를 작성하여 CRUD 작업을 수행합니다.

4. 프로바이더(`TodoProvider`)를 설정하여 데이터베이스와 UI 간의 상태를 관리합니다.

5. UI를 작성하여 할 일 목록을 표시하고, 할 일을 추가, 수정, 삭제할 수 있는 기능을 제공합니다.

6. 메인 파일에서 MultiProvider를 설정하여 앱을 실행합니다.

 

 

반응형

+ Recent posts