Proyek Ketiga: Membuat Aplikasi Todo List Lengkap dengan Fitur Hapus Tugas

📅 12 Agustus 2026 ☕ 3 min read
Proyek Ketiga: Membuat Aplikasi Todo List Lengkap dengan Fitur Hapus Tugas

Proyek Ketiga: Membuat Aplikasi Todo List Lengkap dengan Fitur Hapus Tugas

Pada proyek terakhir dari seri dasar Flutter ini, kita akan menyempurnakan alur pengolahan data lokal dengan membangun Aplikasi Todo List.

Aplikasi ini tidak hanya menerima input data baru, tetapi juga mengimplementasikan operasi manipulasi data lengkap: menandai tugas yang selesai (update status) dan menghapus tugas dari daftar (delete item).

Konsep Utama yang Diterapkan

  • Model Data kustom: Membuat kelas Dart tersendiri (TodoItem) untuk memuat properti nama tugas dan status penyelesaiannya secara terstruktur.

  • Manipulasi List Dinamis: Memperbarui properti elemen secara spesifik berdasarkan indeksnya, serta menghapus data menggunakan fungsi List.removeAt().

  • Efek Visual Kondisional: Mengubah gaya teks (text decoration) menjadi dicoret (strikethrough) ketika status tugas dinyatakan selesai.

Kode Lengkap Proyek

Dart
 
import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    debugShowCheckedModeBanner: false,
    home: TodoApp(),
  ));
}

// 1. Model Data untuk Tugas
class TodoItem {
  String title;
  bool isDone;

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

class TodoApp extends StatefulWidget {
  const TodoApp({super.key});

  @override
  State<TodoApp> createState() => _TodoAppState();
}

class _TodoAppState extends State<TodoApp> {
  // Penampung data berjenis kustom TodoItem
  final List<TodoItem> _todos = [];
  final TextEditingController _controller = TextEditingController();

  // Menambah tugas baru
  void _addTodo() {
    final text = _controller.text.trim();
    if (text.isNotEmpty) {
      setState(() {
        _todos.add(TodoItem(title: text));
        _controller.clear();
      });
    }
  }

  // Mengubah status selesai/belum selesai
  void _toggleTodoStatus(int index) {
    setState(() {
      _todos[index].isDone = !_todos[index].isDone;
    });
  }

  // Menghapus tugas dari list
  void _deleteTodo(int index) {
    setState(() {
      _todos.removeAt(index);
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Todo List Lengkap'),
        backgroundColor: Colors.teal,
        foregroundColor: Colors.white,
      ),
      body: Column(
        children: [
          // Form Input Tugas
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(
                      hintText: 'Tambah tugas baru...',
                      border: OutlineInputBorder(),
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _addTodo,
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.teal,
                    foregroundColor: Colors.white,
                    padding: const EdgeInsets.symmetric(vertical: 16),
                  ),
                  child: const Text('Tambah'),
                ),
              ],
            ),
          ),

          // Daftar Tugas
          Expanded(
            child: _todos.isEmpty
                ? const Center(
                    child: Text(
                      'Belum ada tugas. Yey!',
                      style: TextStyle(color: Colors.grey),
                    ),
                  )
                : ListView.builder(
                    itemCount: _todos.length,
                    itemBuilder: (context, index) {
                      final todo = _todos[index];
                      return Card(
                        margin: const EdgeInsets.symmetric(
                          horizontal: 16,
                          vertical: 4,
                        ),
                        child: ListTile(
                          // Checkbox untuk mengubah status
                          leading: Checkbox(
                            value: todo.isDone,
                            onChanged: (_) => _toggleTodoStatus(index),
                          ),
                          // Judul tugas dengan gaya kondisional
                          title: Text(
                            todo.title,
                            style: TextStyle(
                              decoration: todo.isDone
                                  ? TextDecoration.lineThrough
                                  : TextDecoration.none,
                              color: todo.isDone ? Colors.grey : Colors.black,
                            ),
                          ),
                          // Tombol Hapus
                          trailing: IconButton(
                            icon: const Icon(
                              Icons.delete,
                              color: Colors.redAccent,
                            ),
                            onPressed: () => _deleteTodo(index),
                          ),
                        ),
                      );
                    },
                  ),
          ),
        ],
      ),
    );
  }
}

Bedah Jalurnya Kode

  1. Penggunaan Class TodoItem: Memisahkan logika struktur data dari UI. Ini memungkinkan satu item memuat dua informasi sekaligus (title dan status isDone).

  2. Toggle Checkbox (_toggleTodoStatus): Ketika Checkbox diklik, status isDone dari item pada indeks tertentu dibalik (!todo.isDone). setState() kemudian memperbarui tampilan teks menjadi dicoret secara otomatis.

  3. Penghapusan Data (_deleteTodo): Menggunakan _todos.removeAt(index) untuk menghapus elemen pada indeks spesifik secara efisien.


+