Membuat Aplikasi Todo List Sederhana -

 Membangun Aplikasi Todo List Flutter




___________________________________________________________________________________

1. Fungsi main() dan runApp()


void main() {

  runApp(MyApp());

}

main() adalah titik awal aplikasi Flutter.

runApp() menjalankan widget utama aplikasi, yaitu MyApp.


2. Widget MyApp


class MyApp extends StatelessWidget {

  @override

  Widget build(BuildContext context) {

    return MaterialApp(

      title: 'Caca Last List',

      debugShowCheckedModeBanner: false,

      theme: ThemeData(

        primaryColor: Color(0xFF3B82F6),

        scaffoldBackgroundColor: Colors.transparent,

        fontFamily: 'Roboto',

      ),

      home: TodoPage(),

    );

  }

}


StatelessWidget → widget statis, tidak menyimpan perubahan state.

MaterialApp → root widget untuk aplikasi berbasis Material Design.

ThemeData → mengatur tema warna, font, dan tampilan global.

home → halaman utama aplikasi (TodoPage).



3. Widget TodoPage (StatefulWidget)


class TodoPage extends StatefulWidget {

  @override

  _TodoPageState createState() => _TodoPageState();

}


Menggunakan StatefulWidget karena data todo dan background dapat berubah.

State disimpan di class _TodoPageState.



4. Controller dan Variabel State


TextEditingController todoController = TextEditingController();

TextEditingController bgController = TextEditingController();

TextEditingController editController = TextEditingController();


List<String> todoList = [];

String backgroundUrl = 'https://images.unsplash.com/photo-1503264116251-35a269479413';


Fungsi masing-masing:

TextEditingController → mengambil dan mengontrol input dari TextField.

todoList → menyimpan daftar tugas.

backgroundUrl → URL gambar background aplikasi.



5. initState()


@override

void initState() {

  super.initState();

  loadData();

}


Dipanggil sekali saat widget dibuat.

Digunakan untuk memuat data dari SharedPreferences.



6. SharedPreferences (Penyimpanan Lokal)


Memuat Data


Future<void> loadData() async {

  SharedPreferences prefs = await SharedPreferences.getInstance();

  List<String>? storedList = prefs.getStringList('todoList');

  String? storedBg = prefs.getString('backgroundUrl');

___________________________________________________________________________________

  setState(() {

    todoList = storedList ?? [];

    backgroundUrl = storedBg ?? backgroundUrl;

  });

}

Mengambil data todo dan background yang tersimpan.

setState() digunakan agar UI ikut diperbarui.


Menyimpan Data


Future<void> saveData() async {

  Shared


___________________________________________________________________________________


ini code full nya


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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Caca Last List',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primaryColor: const Color(0xFF3B82F6),
        scaffoldBackgroundColor: Colors.transparent,
        fontFamily: 'Roboto',
      ),
      home: const TodoPage(),
    );
  }
}

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

  @override
  State<TodoPage> createState() => _TodoPageState();
}

class _TodoPageState extends State<TodoPage> {
  final TextEditingController todoController = TextEditingController();
  final TextEditingController bgController = TextEditingController();
  final TextEditingController editController = TextEditingController();

  List<String> todoList = [];
  String backgroundUrl =
      'https://images.unsplash.com/photo-1503264116251-35a269479413';

  @override
  void initState() {
    super.initState();
    loadData();
  }

  // =======================
  // DATA PERSISTENCE
  // =======================
  Future<void> loadData() async {
    final prefs = await SharedPreferences.getInstance();
    final storedList = prefs.getStringList('todoList');
    final storedBg = prefs.getString('backgroundUrl');

    setState(() {
      todoList = storedList ?? [];
      backgroundUrl = storedBg ?? backgroundUrl;
    });
  }

  Future<void> saveData() async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setStringList('todoList', todoList);
    await prefs.setString('backgroundUrl', backgroundUrl);
  }

  // =======================
  // TODO FUNCTIONS
  // =======================
  void tambahTodo() {
    if (todoController.text.isEmpty) return;

    setState(() {
      todoList.add(todoController.text);
      todoController.clear();
    });
    saveData();
  }

  void hapusTodo(int index) {
    setState(() {
      todoList.removeAt(index);
    });
    saveData();
  }

  void editTodo(int index) {
    editController.text = todoList[index];

    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(24),
        ),
        title: const Text('Edit Tugas'),
        content: TextField(
          controller: editController,
          decoration: InputDecoration(
            hintText: 'Ubah tugas...',
            filled: true,
            fillColor: Colors.blueGrey[50],
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(20),
              borderSide: BorderSide.none,
            ),
          ),
        ),
        actions: [
          TextButton(
            onPressed: () {
              editController.clear();
              Navigator.pop(context);
            },
            child: const Text('Batal'),
          ),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: const Color(0xFF3B82F6),
              shape: const StadiumBorder(),
            ),
            onPressed: () {
              if (editController.text.isNotEmpty) {
                setState(() {
                  todoList[index] = editController.text;
                });
                saveData();
              }
              editController.clear();
              Navigator.pop(context);
            },
            child: const Text('Simpan'),
          ),
        ],
      ),
    );
  }

  void showBackgroundDialog() {
    showDialog(
      context: context,
      builder: (_) => AlertDialog(
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(24),
        ),
        title: const Text('Ganti Background'),
        content: TextField(
          controller: bgController,
          decoration: InputDecoration(
            hintText: 'Tempel URL gambar...',
            filled: true,
            fillColor: Colors.blueGrey[50],
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(20),
              borderSide: BorderSide.none,
            ),
          ),
        ),
        actions: [
          TextButton(
            onPressed: () {
              bgController.clear();
              Navigator.pop(context);
            },
            child: const Text('Batal'),
          ),
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              backgroundColor: const Color(0xFF3B82F6),
              shape: const StadiumBorder(),
            ),
            onPressed: () {
              if (bgController.text.isNotEmpty) {
                setState(() {
                  backgroundUrl = bgController.text;
                });
                saveData();
              }
              bgController.clear();
              Navigator.pop(context);
            },
            child: const Text('Ganti'),
          ),
        ],
      ),
    );
  }

  // =======================
  // BUILD UI
  // =======================
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Stack(
          children: [
            // BACKGROUND
            Container(
              decoration: BoxDecoration(
                image: DecorationImage(
                  image: NetworkImage(backgroundUrl),
                  fit: BoxFit.cover,
                ),
              ),
            ),

            // OVERLAY
            Container(color: Colors.black.withOpacity(0.25)),

            Column(
              children: [
                // APP BAR
                Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                  color: const Color(0xFF3B82F6),
                  child: Stack(
                    alignment: Alignment.center,
                    children: [
                      const Text(
                        'Caca Last List',
                        style: TextStyle(
                          fontWeight: FontWeight.bold,
                          fontSize: 20,
                          color: Colors.white,
                        ),
                      ),
                      Positioned(
                        right: 0,
                        child: IconButton(
                          icon: const Icon(
                            Icons.auto_awesome,
                            color: Colors.white,
                          ),
                          onPressed: showBackgroundDialog,
                        ),
                      ),
                    ],
                  ),
                ),

                // INPUT TODO
                Padding(
                  padding: const EdgeInsets.all(16),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 16, vertical: 10),
                    decoration: BoxDecoration(
                      color: Colors.blueGrey.shade900.withOpacity(0.85),
                      borderRadius: BorderRadius.circular(30),
                    ),
                    child: Row(
                      children: [
                        Expanded(
                          child: TextField(
                            controller: todoController,
                            style: const TextStyle(color: Colors.white),
                            decoration: InputDecoration(
                              hintText: 'Tulis tugas...',
                              hintStyle:
                                  TextStyle(color: Colors.grey.shade300),
                              border: InputBorder.none,
                            ),
                          ),
                        ),
                        IconButton(
                          icon: const Icon(
                            Icons.add_circle,
                            color: Colors.orangeAccent,
                            size: 36,
                          ),
                          onPressed: tambahTodo,
                        ),
                      ],
                    ),
                  ),
                ),

                // LIST TODO
                Expanded(
                  child: Padding(
                    padding: const EdgeInsets.symmetric(horizontal: 16),
                    child: todoList.isEmpty
                        ? const Center(
                            child: Text(
                              'Belum ada tugas!',
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 18,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          )
                        : ListView.builder(
                            itemCount: todoList.length,
                            itemBuilder: (_, index) {
                              return Container(
                                margin: const EdgeInsets.only(bottom: 14),
                                decoration: BoxDecoration(
                                  color: Colors.blueGrey.shade800
                                      .withOpacity(0.9),
                                  borderRadius: BorderRadius.circular(28),
                                ),
                                child: ListTile(
                                  contentPadding:
                                      const EdgeInsets.symmetric(
                                          horizontal: 20, vertical: 4),
                                  leading: CircleAvatar(
                                    radius: 22,
                                    backgroundColor:
                                        Colors.tealAccent.shade700,
                                    child: Text(
                                      '${index + 1}',
                                      style: const TextStyle(
                                        color: Colors.black,
                                        fontWeight: FontWeight.bold,
                                      ),
                                    ),
                                  ),
                                  title: Text(
                                    todoList[index],
                                    style: const TextStyle(
                                      fontSize: 16,
                                      fontWeight: FontWeight.w600,
                                      color: Colors.white,
                                    ),
                                  ),
                                  trailing: Row(
                                    mainAxisSize: MainAxisSize.min,
                                    children: [
                                      IconButton(
                                        icon: const Icon(
                                          Icons.edit_note,
                                          color: Colors.orangeAccent,
                                        ),
                                        onPressed: () => editTodo(index),
                                      ),
                                      IconButton(
                                        icon: const Icon(
                                          Icons.delete_forever,
                                          color: Colors.redAccent,
                                        ),
                                        onPressed: () => hapusTodo(index),
                                      ),
                                    ],
                                  ),
                                ),
                              );
                            },
                          ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}

___________________________________________________________________________________

ini link nya ...


Semoga artikel ini bisa membantu kamu memahami Flutter lebih dalam dan menjadi bekal untuk membangun aplikasi yang lebih baik. Teruslah bereksperimen, belajar, dan jangan takut mencoba fitur-fitur baru. Sampai jumpa di pembahasan Flutter berikutnya—selamat ngoding! 🚀


Komentar

Postingan populer dari blog ini

Belajar Flutter Biar Gak Cupu: Bikin App Ada Foto + Tombol SnackBar

membuat aplikasi sederhana dengan konsep CRUD - ngawiflix

Membuat Aplikasi Flutter Daftar Film MCU dengan Navigasi & Layout responsif