🎯 Kode Akhir: CRUD Todo
Berikut adalah kode akhir untuk Halaman Home yang menerapkan fungsionalitas CRUD (Create, Read/Retrieve, Update, Delete) pada Todo yang telah kita buat.
📃 home_screen.dart
dart
import 'package:flutter/material.dart';
import '../models/todo.dart';
class HomeScreen extends StatefulWidget {
const HomeScreen({super.key});
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// List: sekumpulan todo yang akan ditampilkan
final List<Todo> todoList = [
Todo(
completed: false,
title: "Belajar",
description: "Belajar Desain Flutter",
),
Todo(
completed: true,
title: "Olahraga",
description: "Lari pagi selama 30 menit",
),
Todo(
completed: false,
title: "Makan Siang",
description: "Makan siang sehat dengan sayuran",
),
Todo(
completed: true,
title: "Bekerja",
description: "Menyelesaikan project mobile app",
),
Todo(
completed: true,
title: "Membaca Buku",
description: "Membaca buku 'Clean Code'",
),
Todo(
completed: false,
title: "Berkebun",
description: "Menyiram tanaman di taman rumah",
),
Todo(
completed: true,
title: "Belajar Bahasa",
description: "Latihan percakapan bahasa Inggris",
),
];
final TextEditingController titleController = TextEditingController();
final TextEditingController descriptionController = TextEditingController();
// Fungsi: aksi untuk menghapus todo dari list
// ↓ ↓ ↓ ↓ ↓ ↓
void hapusTodo(Todo todo) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text("Konfirmasi Hapus"),
icon: Icon(
Icons.warning_rounded,
color: Colors.red[400],
size: 120,
),
content: Text(
"Apakah anda yakin untuk menghapus todo \"${todo.title}\" ",
textAlign: TextAlign.center,
),
actions: [
TextButton(
onPressed: () {
// action jika tombol batal ditekan
Navigator.pop(context);
},
child: const Text(
"Batal",
style: TextStyle(color: Colors.blueGrey),
),
),
ElevatedButton(
onPressed: () {
// action jika tombol komfirmasi hapus ditekan
setState(() {
todoList.remove(todo);
});
Navigator.pop(context);
},
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
child: const Text(
"Hapus",
style: TextStyle(color: Colors.white),
),
),
],
);
},
);
}
// Fungsi: aksi untuk mengeatur checklis dari todo
// ↓↓↓↓↓↓
void checklisTodo(Todo todo) {
Todo todoBaru = Todo(
title: todo.title,
description: todo.description,
completed: !todo.completed,
);
int indexTodo = todoList.indexOf(todo);
setState(() {
todoList[indexTodo] = todoBaru;
});
}
// Fungsi: aksi untuk menambahkan todo kedalam list
// ↓↓↓↓↓↓
void tambahTodo(Todo todo) {
setState(() {
todoList.add(todo);
});
}
// Fungsi: aksi untuk memperbarui todo dari list
// ↓↓↓↓↓↓
void updateTodo(Todo currentTodo, Todo updatedTodo) {
int indexTodo = todoList.indexOf(currentTodo);
setState(() {
todoList[indexTodo].title = updatedTodo.title;
todoList[indexTodo].description = updatedTodo.description;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFececf4),
// Widget: App bar untuk menampilkan ikon menu dan profil
// ↓↓↓↓↓↓
appBar: AppBar(
backgroundColor: const Color(0xFFececf4),
scrolledUnderElevation: 0,
// METHOD 2 TO CREATE APPBAR:
// title: SizedBox(
// child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
// children: [
// const Icon(Icons.menu),
// Container(
// decoration: BoxDecoration(
// color: Colors.blue[100],
// borderRadius: BorderRadius.circular(30),
// ),
// padding: const EdgeInsets.all(10),
// child: const Icon(
// Icons.person,
// color: Colors.blue,
// ),
// ),
// ],
// ),
// ),
leading: const Icon(Icons.menu),
actions: [
Container(
decoration: BoxDecoration(
color: Colors.blue[100],
borderRadius: BorderRadius.circular(30),
),
padding: const EdgeInsets.all(10),
child: const Icon(
Icons.person,
color: Colors.blue,
),
),
],
actionsPadding: const EdgeInsets.only(right: 20),
),
// Widget: isi dari halaman seperti search, dan list todo,
// ↓↓↓↓↓↓
body: SizedBox(
width: MediaQuery.sizeOf(context).width,
height: MediaQuery.sizeOf(context).height,
child: Column(
children: [
// Widget: untuk bagian search box
// ↓↓↓↓↓↓
Container(
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.white,
),
margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
padding: const EdgeInsets.only(top: 5, left: 5, right: 5),
child: const TextField(
decoration: InputDecoration(
contentPadding: EdgeInsets.only(top: 12, right: 20),
prefixIcon: Icon(Icons.search),
border: InputBorder.none,
hintText: "Search",
),
),
),
// Widget: list dari todo
// ↓↓↓↓↓↓
Expanded(
child: ListView.builder(
padding: const EdgeInsets.only(
top: 20, left: 20, right: 20, bottom: 50),
itemCount: todoList.length + 1,
itemBuilder: (context, index) {
// HINT:
if (index == 0) {
if (todoList.isEmpty) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
listViewTitle(),
const Text("Empty"),
],
);
}
return listViewTitle();
} else {
final Todo todoItem = todoList[index - 1];
return cardItem(todoItem);
}
},
),
),
],
),
),
// Widget: floating button/tombol "tambah" di pojok kanan bawah
// ↓↓↓↓↓↓
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.blue[300],
onPressed: () async {
showModal();
},
child: const Icon(
Icons.add,
color: Colors.white,
),
),
);
}
// Fungsi: untuk menampilkan tulisan/judul "All ToDo's"
Widget listViewTitle() {
return Container(
margin: const EdgeInsets.only(bottom: 20),
width: double.maxFinite,
child: const Text(
"All ToDo's",
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.left,
),
);
}
// Fungsi: untuk menampilkan satu buah todo
// ↓↓↓↓↓↓
Widget cardItem(Todo todoItem) {
return Padding(
padding: const EdgeInsets.only(bottom: 20),
child: Material(
color: Colors.transparent,
child: ListTile(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
contentPadding:
const EdgeInsets.symmetric(vertical: 10, horizontal: 20),
onTap: () {
checklisTodo(todoItem);
},
onLongPress: () {
showModal(todo: todoItem);
},
tileColor: Colors.white,
leading: Icon(
todoItem.completed
? Icons.check_box_rounded
: Icons.check_box_outline_blank_rounded,
color: Colors.blue,
),
title: Text(todoItem.title),
subtitle: Text(
todoItem.description,
style: const TextStyle(color: Colors.grey),
),
trailing: IconButton(
onPressed: () {
hapusTodo(todoItem);
},
icon: Icon(
Icons.delete,
color: Colors.red[300],
),
),
),
),
);
}
// Fungsi: untuk menampilkan modal bottom
// ↓↓↓↓↓↓
void showModal({
Todo? todo,
}) async {
if (todo != null) {
titleController.text = todo.title;
descriptionController.text = todo.description;
}
showModalBottomSheet(
context: context,
// isScrollControlled: true,
builder: (context) {
final keyboardBottomPadding = MediaQuery.of(context).viewInsets.bottom;
return Padding(
padding: EdgeInsets.only(
left: 25, right: 25, bottom: 25 + keyboardBottomPadding),
child: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
// Widget: label/tulisan "Todo Title" untuk TextField "Todo Title"
// ↓↓↓↓↓↓
const Padding(
padding: EdgeInsets.only(top: 33, bottom: 8),
child: Text(
"ToDo Title",
),
),
// Widget: TextField "Email"
// ↓↓↓↓↓↓
TextField(
controller: titleController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(
color: Colors.black,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
),
),
// Widget: label/tulisan "ToDo Description" untuk TextField "ToDo Description"
// ↓↓↓↓↓↓
const Padding(
padding: EdgeInsets.only(top: 33, bottom: 8),
child: Text(
"ToDo Description",
),
),
// Widget: TextField "ToDo Description"
// ↓↓↓↓↓↓
TextField(
controller: descriptionController,
decoration: InputDecoration(
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
borderSide: const BorderSide(
color: Colors.black,
),
),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
),
),
// Widget: untuk membuat button "Add Todo"
// ↓↓↓↓↓↓
Container(
height: 82,
width: double.maxFinite,
padding: const EdgeInsets.only(top: 24),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(15),
),
backgroundColor: Colors.blue,
),
onPressed: () {
if (todo != null) {
// Dia akan mengupdate Todo
updateTodo(
todo,
Todo(
title: titleController.text,
description: descriptionController.text,
completed: false,
),
);
} else {
// Dia akan menambahkan Todo baru
tambahTodo(
Todo(
title: titleController.text,
description: descriptionController.text,
completed: false,
),
);
}
Navigator.pop(context);
},
child: const Text("Add ToDo"),
),
),
],
),
),
);
},
);
}
}
WRI Roadmap