이 앱은 “할 일 목록 (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'),
),
],
),
),
);
}
}
'프론트엔드 > Flutter' 카테고리의 다른 글
flutter StatelessWidget 와 StatefulWidget 차이 및 설명 (0) | 2024.07.24 |
---|---|
flutter firebase 를 이용한 이메일 로그인 구현 (2) | 2024.07.23 |
flutter scrollview pagination 예제 및 정리 (2) | 2024.07.22 |
flutter mvvm 패턴 정리 및 예제 (0) | 2024.07.22 |
flutter form 제출 및 라우터 이동 (0) | 2024.07.22 |