반응형

이 앱은 “할 일 목록 (To-Do List)” 앱으로, 기본적인 CRUD(생성, 읽기, 업데이트, 삭제) 작업을 포함할 것입니다.

 

샘플 앱: To-Do List

 

1. 프로젝트 생성

 

Flutter 프로젝트를 생성합니다.

flutter create todo_app
cd todo_app

 

2. 주요 패키지 추가

 

pubspec.yaml 파일에 필요한 패키지를 추가합니다. 이 예제에서는 provider를 상태 관리를 위해 사용합니다.

dev_dependencies:
  flutter_test:
    sdk: flutter
  provider:
  uuid:

이후 flutter pub get 명령어를 실행해 패키지를 설치합니다.

 

3. 모델 정의

 

할 일 항목의 모델 클래스를 정의합니다.

// lib/models/todo.dart
class Todo {
  String id;
  String title;
  bool isDone;

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

4. 상태 관리 설정

 

할 일 목록을 관리하는 ChangeNotifier 클래스를 정의합니다.

import 'package:flutter/foundation.dart';
import '../model/todo.dart';

// 할 일 목록을 관리하는 ChangeNotifier 클래스
class TodoProvider with ChangeNotifier {
  List<Todo> _todos = [];

  List<Todo> get todos => _todos;

  // 할 일 항목을 추가하는 메서드
  void addTodo(Todo todo) {
    _todos.add(todo);
    notifyListeners();
  }

  // 할 일 항목의 완료 상태를 토글하는 메서드
  void toggleTodoStatus(String id) {
    final index = _todos.indexWhere((todo) => todo.id == id);
    if (index != -1) {
      _todos[index].isDone = !_todos[index].isDone;
      notifyListeners();
    }
  }

  // 할 일 항목을 제거하는 메서드
  void removeTodo(String id) {
    _todos.removeWhere((todo) => todo.id == id);
    notifyListeners();
  }
// 업데이트
  void updateTodo(Todo updatedTodo) {
    final index = _todos.indexWhere((todo) => todo.id == updatedTodo.id);
    if (index != -1) {
      _todos[index] = updatedTodo;
      notifyListeners();
    }
  }
}

참고 if 문에 -1 확인 이유

f 문을 사용하여 index-1이 아닌지 확인하는 이유는 indexWhere 메서드가 일치하는 항목을 찾지 못할 때 -1을 반환하기 때문입니다. index-1이면 _todos[index]는 유효하지 않으며, 이 경우 배열에 접근하려고 하면 오류가 발생합니다. 따라서 if 문은 안전한 코드를 작성하는 데 중요합니다.

 

그럼에도 불구하고, indexWhere가 항상 올바른 값을 반환한다고 확신할 수 있다면 이 조건문을 생략할 수 있지만, 이는 코드의 안정성과 유연성을 감소시킬 수 있습니다. 일반적으로는 if 문을 포함하는 것이 좋습니다.

 

5. 메인 파일 설정

 

프로바이더를 설정하고 메인 파일을 구성합니다.

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

void main() {
  runApp(
    MultiProvider(
      providers: [
        // TodoProvider를 ChangeNotifierProvider로 제공
        ChangeNotifierProvider(create: (_) => TodoProvider()),
      ],
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'To-Do App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: TodoListScreen(),
    );
  }
}

6. UI 작성

 

할 일 목록 화면을 작성합니다.

// lib/screens/todo_list_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_todo/screen/todo_edit_screen.dart';
import 'package:provider/provider.dart';
import 'package:uuid/uuid.dart';

import '../model/todo.dart';
import '../providers/todo_provider.dart';

class TodoListScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final todoProvider = Provider.of<TodoProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('To-Do List'),
      ),
      body: 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.toggleTodoStatus(todoProvider.todos[i].id); // 완료 상태 토글
            },
          ),
          onTap: () {
            // 수정 페이지로 이동
            Navigator.of(context).push(
              MaterialPageRoute(
                builder: (context) => EditTodoScreen(todo: todoProvider.todos[i]),
              ),
            );
          },
          onLongPress: () {
            todoProvider.removeTodo(todoProvider.todos[i].id); // 할 일 항목 제거
          },
        ),
      ),
      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(
                          id: Uuid().v4(),
                          title: controller.text,
                        );
                        todoProvider.addTodo(newTodo);
                        Navigator.of(ctx).pop();
                      }
                    },
                    child: Text('Add'),
                  ),
                ],
              );
            },
          );
        },
        child: Icon(Icons.add),
      ),
    );
  }
}

7. 수정 페이지 생성

 

새로운 파일 edit_todo_screen.dart를 생성합니다.

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../model/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'),
            ),
          ],
        ),
      ),
    );
  }
}

 

 

반응형

+ Recent posts