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
Posting Komentar