Flutter
Project Flutter Mobile dengan Vibe Coding: Habit Tracker
Tutorial project Flutter Mobile Habit Tracker dengan pendekatan vibe coding: ide, MVP, prompt, struktur folder, model Habit, local storage, UI, progress harian, debugging, dan checklist testing.
Project Flutter Mobile dengan Vibe Coding: Habit Tracker
Di materi ini kita akan membuat project Flutter Mobile bernama Habit Tracker dengan pendekatan vibe coding.
Habit Tracker adalah aplikasi untuk mencatat kebiasaan harian. Contoh habit:
- minum air 8 gelas
- membaca buku
- olahraga
- belajar Flutter
- tidur sebelum jam 11 malam
Project ini cocok untuk latihan karena fiturnya sederhana, tetapi konsepnya banyak:
- model data
- form input
- list data
- toggle status harian
- local storage
- state lokal
- refactor service
- review kode AI
- checklist testing
Targetnya bukan langsung membuat aplikasi sempurna. Targetnya adalah belajar alur membuat aplikasi mobile dari nol sampai bisa dipakai.
1. Hasil Akhir yang Ingin Dibuat
Fitur versi pertama:
- user bisa menambah habit
- user bisa melihat daftar habit
- user bisa menandai habit selesai hari ini
- user bisa menghapus habit
- user bisa melihat jumlah habit selesai hari ini
- data tersimpan walaupun aplikasi ditutup
Contoh tampilan sederhana:
Habit Tracker
Hari ini
3 dari 5 habit selesai
[+] Tambah Habit
[x] Minum air
[ ] Belajar Flutter
[x] Baca buku
[ ] Olahraga
[x] Tidur cukup
Setelah versi ini selesai, nanti bisa dikembangkan menjadi:
- streak harian
- kalender habit
- statistik mingguan
- reminder
- Firebase sync
- login user
2. Cara Vibe Coding untuk Project Ini
Kita tidak akan meminta AI membuat semuanya sekaligus.
Kita pakai pendekatan bertahap:
Tahap 1 - Definisikan fitur MVP
Tahap 2 - Buat model Habit
Tahap 3 - Buat UI list habit
Tahap 4 - Tambah form add habit
Tahap 5 - Tambah toggle selesai hari ini
Tahap 6 - Tambah delete habit
Tahap 7 - Tambah local storage
Tahap 8 - Refactor kode
Tahap 9 - Testing manual
Tahap 10 - Catatan dokumentasi
Kenapa bertahap?
Karena kalau semua dibuat sekaligus:
- sulit membaca kode
- sulit tahu error dari bagian mana
- AI bisa membuat struktur terlalu kompleks
- pemula mudah hanya copy-paste
Vibe coding yang bagus adalah memberi instruksi kecil, menjalankan hasilnya, lalu lanjut.
3. Prompt Awal Project
Gunakan prompt ini untuk memulai project dengan AI:
Saya ingin membuat project Flutter Mobile untuk belajar vibe coding.
Project: Habit Tracker.
Level saya masih belajar Flutter.
Target versi MVP:
- tambah habit
- tampilkan daftar habit
- tandai habit selesai hari ini
- hapus habit
- simpan data lokal
- tampilkan progress selesai hari ini
Tolong bantu saya secara bertahap.
Jangan langsung buat semua kode.
Mulai dari:
1. daftar fitur MVP
2. struktur folder sederhana
3. model Habit
4. checklist testing
Prompt ini bagus karena:
- menjelaskan project
- menjelaskan level
- menjelaskan fitur
- meminta step-by-step
- membatasi agar AI tidak langsung terlalu besar
4. Membuat Project Flutter
Command:
flutter create habit_tracker
cd habit_tracker
flutter run
Setelah project berjalan, bersihkan lib/main.dart agar siap dipakai.
Prompt ke AI:
Buatkan main.dart Flutter sederhana untuk project Habit Tracker.
Gunakan MaterialApp, debugShowCheckedModeBanner false,
theme warna hijau atau biru, dan home HabitPage.
Jangan buat fitur dulu, cukup struktur awal.
Contoh main.dart:
import 'package:flutter/material.dart';
import 'pages/habit_page.dart';
void main() {
runApp(const HabitTrackerApp());
}
class HabitTrackerApp extends StatelessWidget {
const HabitTrackerApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Habit Tracker',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const HabitPage(),
);
}
}
5. Struktur Folder
Untuk pemula, gunakan struktur sederhana:
lib/
main.dart
models/
habit.dart
pages/
habit_page.dart
services/
habit_storage_service.dart
Penjelasan:
main.dart: titik awal aplikasimodels/habit.dart: bentuk data habitpages/habit_page.dart: tampilan utama aplikasiservices/habit_storage_service.dart: simpan dan baca data lokal
Struktur ini cukup rapi tanpa terlalu rumit.
6. Membuat Model Habit
File:
lib/models/habit.dart
Isi:
class Habit {
final String id;
final String title;
final bool isDoneToday;
final DateTime createdAt;
final DateTime? lastCompletedAt;
const Habit({
required this.id,
required this.title,
required this.isDoneToday,
required this.createdAt,
this.lastCompletedAt,
});
Habit copyWith({
String? id,
String? title,
bool? isDoneToday,
DateTime? createdAt,
DateTime? lastCompletedAt,
}) {
return Habit(
id: id ?? this.id,
title: title ?? this.title,
isDoneToday: isDoneToday ?? this.isDoneToday,
createdAt: createdAt ?? this.createdAt,
lastCompletedAt: lastCompletedAt ?? this.lastCompletedAt,
);
}
}
Penjelasan:
id: identitas unik habittitle: nama habitisDoneToday: status selesai hari inicreatedAt: kapan habit dibuatlastCompletedAt: kapan terakhir habit diselesaikancopyWith: membuat object baru dari data lama
Kenapa perlu copyWith?
Karena di Flutter lebih rapi jika data dibuat sebagai object immutable. Saat ingin mengubah status, kita membuat object baru.
Contoh:
final updatedHabit = habit.copyWith(isDoneToday: true);
7. Prompt untuk Memahami Model
Setelah AI membuat model, jangan langsung lanjut. Minta penjelasan.
Prompt:
Jelaskan model Habit ini untuk pemula.
Bahas:
- fungsi setiap field
- kenapa id dibutuhkan
- kenapa pakai final
- kenapa copyWith berguna
- contoh cara membuat Habit baru
- contoh cara toggle isDoneToday
Ini penting agar kamu tidak hanya memakai model, tetapi paham kenapa bentuknya seperti itu.
8. Membuat UI Awal HabitPage
File:
lib/pages/habit_page.dart
Isi awal:
import 'package:flutter/material.dart';
import '../models/habit.dart';
class HabitPage extends StatefulWidget {
const HabitPage({super.key});
@override
State<HabitPage> createState() => _HabitPageState();
}
class _HabitPageState extends State<HabitPage> {
final List<Habit> habits = [];
int get completedCount {
return habits.where((habit) => habit.isDoneToday).length;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Habit Tracker'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Hari ini',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text(
'$completedCount dari ${habits.length} habit selesai',
),
const SizedBox(height: 16),
Expanded(
child: habits.isEmpty
? const Center(
child: Text('Belum ada habit'),
)
: ListView.builder(
itemCount: habits.length,
itemBuilder: (context, index) {
final habit = habits[index];
return ListTile(
title: Text(habit.title),
leading: Checkbox(
value: habit.isDoneToday,
onChanged: null,
),
);
},
),
),
],
),
),
);
}
}
Saat ini belum ada fitur tambah habit. Kita hanya menyiapkan tampilan list.
Checklist:
- aplikasi bisa dijalankan
- halaman Habit Tracker muncul
- empty state muncul
- tidak ada error import
9. Menambah Data Dummy
Sebelum membuat form, gunakan data dummy agar UI terlihat.
Ubah list:
final List<Habit> habits = [
Habit(
id: '1',
title: 'Belajar Flutter',
isDoneToday: false,
createdAt: DateTime.now(),
),
Habit(
id: '2',
title: 'Minum air',
isDoneToday: true,
createdAt: DateTime.now(),
lastCompletedAt: DateTime.now(),
),
];
Tujuannya:
- melihat tampilan list
- melihat progress berubah
- mengecek checkbox
- memastikan layout tidak kosong
Setelah UI aman, data dummy nanti dihapus.
10. Menambah Form Add Habit
Kita buat input sederhana di bagian atas.
Tambahkan controller:
final TextEditingController habitController = TextEditingController();
Tambahkan dispose:
@override
void dispose() {
habitController.dispose();
super.dispose();
}
Tambahkan function:
void addHabit() {
final title = habitController.text.trim();
if (title.isEmpty) return;
final habit = Habit(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
isDoneToday: false,
createdAt: DateTime.now(),
);
setState(() {
habits.add(habit);
habitController.clear();
});
}
Tambahkan UI form sebelum list:
Row(
children: [
Expanded(
child: TextField(
controller: habitController,
decoration: const InputDecoration(
labelText: 'Habit baru',
border: OutlineInputBorder(),
),
onSubmitted: (_) => addHabit(),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: addHabit,
child: const Text('Tambah'),
),
],
),
const SizedBox(height: 16),
Penjelasan:
TextEditingControllermembaca input usertrimmenghapus spasi depan belakang- input kosong tidak ditambahkan
DateTime.now().millisecondsSinceEpochdipakai sebagai id sederhanasetStatemembuat UI update
11. Prompt untuk Fitur Add Habit
Prompt:
Saya sudah punya HabitPage dengan List<Habit>.
Bantu tambahkan fitur add habit.
Syarat:
- pakai TextEditingController
- input kosong tidak boleh masuk
- setelah tambah, input dikosongkan
- id dibuat sederhana dari DateTime.now().millisecondsSinceEpoch
- jelaskan kenapa harus pakai setState
Setelah AI memberi kode, cek:
- controller dibuat
- dispose dipanggil
setStatedipakai- tidak ada habit kosong
- UI tetap rapi
12. Toggle Habit Selesai Hari Ini
Tambahkan function:
void toggleHabit(int index) {
final habit = habits[index];
final isDone = !habit.isDoneToday;
setState(() {
habits[index] = habit.copyWith(
isDoneToday: isDone,
lastCompletedAt: isDone ? DateTime.now() : null,
);
});
}
Ubah checkbox:
Checkbox(
value: habit.isDoneToday,
onChanged: (_) => toggleHabit(index),
),
Ubah title agar habit selesai terlihat dicoret:
Text(
habit.title,
style: TextStyle(
decoration: habit.isDoneToday
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
Penjelasan:
toggleHabitmembalik nilaiisDoneToday- jika selesai,
lastCompletedAtdiisi waktu sekarang - jika dibatalkan,
lastCompletedAtdikosongkan - progress otomatis berubah karena
completedCountmembaca dari list terbaru
13. Menambah Delete Habit
Tambahkan function:
void deleteHabit(int index) {
setState(() {
habits.removeAt(index);
});
}
Tambahkan tombol delete:
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => deleteHabit(index),
),
Untuk UX yang lebih aman, bisa tambah dialog konfirmasi.
Prompt:
Tambahkan fitur delete habit dengan dialog konfirmasi.
Jika user pilih batal, data tidak dihapus.
Jika pilih hapus, habit dihapus dari list.
Gunakan AlertDialog yang sederhana.
14. Kode HabitPage Sementara
Setelah tahap add, toggle, dan delete, bentuk HabitPage bisa seperti ini:
import 'package:flutter/material.dart';
import '../models/habit.dart';
class HabitPage extends StatefulWidget {
const HabitPage({super.key});
@override
State<HabitPage> createState() => _HabitPageState();
}
class _HabitPageState extends State<HabitPage> {
final List<Habit> habits = [];
final TextEditingController habitController = TextEditingController();
int get completedCount {
return habits.where((habit) => habit.isDoneToday).length;
}
@override
void dispose() {
habitController.dispose();
super.dispose();
}
void addHabit() {
final title = habitController.text.trim();
if (title.isEmpty) return;
final habit = Habit(
id: DateTime.now().millisecondsSinceEpoch.toString(),
title: title,
isDoneToday: false,
createdAt: DateTime.now(),
);
setState(() {
habits.add(habit);
habitController.clear();
});
}
void toggleHabit(int index) {
final habit = habits[index];
final isDone = !habit.isDoneToday;
setState(() {
habits[index] = habit.copyWith(
isDoneToday: isDone,
lastCompletedAt: isDone ? DateTime.now() : null,
);
});
}
void deleteHabit(int index) {
setState(() {
habits.removeAt(index);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Habit Tracker'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Hari ini',
style: Theme.of(context).textTheme.headlineSmall,
),
const SizedBox(height: 8),
Text('$completedCount dari ${habits.length} habit selesai'),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: TextField(
controller: habitController,
decoration: const InputDecoration(
labelText: 'Habit baru',
border: OutlineInputBorder(),
),
onSubmitted: (_) => addHabit(),
),
),
const SizedBox(width: 8),
ElevatedButton(
onPressed: addHabit,
child: const Text('Tambah'),
),
],
),
const SizedBox(height: 16),
Expanded(
child: habits.isEmpty
? const Center(child: Text('Belum ada habit'))
: ListView.builder(
itemCount: habits.length,
itemBuilder: (context, index) {
final habit = habits[index];
return ListTile(
leading: Checkbox(
value: habit.isDoneToday,
onChanged: (_) => toggleHabit(index),
),
title: Text(
habit.title,
style: TextStyle(
decoration: habit.isDoneToday
? TextDecoration.lineThrough
: TextDecoration.none,
),
),
trailing: IconButton(
icon: const Icon(Icons.delete),
onPressed: () => deleteHabit(index),
),
);
},
),
),
],
),
),
);
}
}
Ini belum menyimpan data. Kalau aplikasi ditutup, habit akan hilang. Itu normal untuk tahap ini.
15. Upgrade Model Habit untuk JSON
Agar bisa disimpan ke local storage, model perlu toJson dan fromJson.
Update habit.dart:
class Habit {
final String id;
final String title;
final bool isDoneToday;
final DateTime createdAt;
final DateTime? lastCompletedAt;
const Habit({
required this.id,
required this.title,
required this.isDoneToday,
required this.createdAt,
this.lastCompletedAt,
});
factory Habit.fromJson(Map<String, dynamic> json) {
return Habit(
id: json['id'],
title: json['title'],
isDoneToday: json['isDoneToday'] ?? false,
createdAt: DateTime.parse(json['createdAt']),
lastCompletedAt: json['lastCompletedAt'] == null
? null
: DateTime.parse(json['lastCompletedAt']),
);
}
Map<String, dynamic> toJson() {
return {
'id': id,
'title': title,
'isDoneToday': isDoneToday,
'createdAt': createdAt.toIso8601String(),
'lastCompletedAt': lastCompletedAt?.toIso8601String(),
};
}
Habit copyWith({
String? id,
String? title,
bool? isDoneToday,
DateTime? createdAt,
DateTime? lastCompletedAt,
}) {
return Habit(
id: id ?? this.id,
title: title ?? this.title,
isDoneToday: isDoneToday ?? this.isDoneToday,
createdAt: createdAt ?? this.createdAt,
lastCompletedAt: lastCompletedAt ?? this.lastCompletedAt,
);
}
}
Penjelasan:
toJsonmengubah object Habit menjadi MapfromJsonmengubah Map menjadi object HabitDateTimedisimpan sebagai string ISOlastCompletedAtboleh null
Kenapa tanggal diubah ke string?
Karena local storage lebih mudah menyimpan string daripada object DateTime.
16. Install Shared Preferences
Command:
flutter pub add shared_preferences
Package ini dipakai untuk menyimpan data sederhana di device.
Untuk Habit Tracker versi awal, shared_preferences sudah cukup.
17. Membuat HabitStorageService
File:
lib/services/habit_storage_service.dart
Isi:
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/habit.dart';
class HabitStorageService {
static const String _habitsKey = 'habits';
Future<List<Habit>> loadHabits() async {
final prefs = await SharedPreferences.getInstance();
final habitsString = prefs.getString(_habitsKey);
if (habitsString == null) {
return [];
}
final List<dynamic> decodedList = jsonDecode(habitsString);
return decodedList
.map((item) => Habit.fromJson(item as Map<String, dynamic>))
.toList();
}
Future<void> saveHabits(List<Habit> habits) async {
final prefs = await SharedPreferences.getInstance();
final encodedList = habits.map((habit) => habit.toJson()).toList();
final habitsString = jsonEncode(encodedList);
await prefs.setString(_habitsKey, habitsString);
}
}
Penjelasan:
_habitsKeyadalah nama key penyimpananloadHabitsmembaca string JSON dari storage- jika belum ada data, return list kosong
jsonDecodemengubah string menjadi listHabit.fromJsonmengubah item JSON menjadi objectsaveHabitsmengubah list habit menjadi JSON stringprefs.setStringmenyimpan data
18. Prompt untuk Local Storage
Prompt:
Saya sudah punya model Habit dan HabitPage.
Bantu tambahkan local storage dengan shared_preferences.
Tolong buat:
- toJson dan fromJson di Habit
- HabitStorageService
- loadHabits saat halaman dibuka
- saveHabits setelah add, toggle, delete
- loading state saat data dibaca
- jelaskan alur datanya
Minta AI membuat satu tahap dulu:
Mulai dari HabitStorageService saja.
Setelah itu tunggu saya jalankan.
19. Integrasi Storage ke HabitPage
Tambahkan service:
final HabitStorageService storageService = HabitStorageService();
Ubah list agar bisa diisi dari storage:
List<Habit> habits = [];
bool isLoading = true;
Tambahkan initState:
@override
void initState() {
super.initState();
loadHabits();
}
Tambahkan function load:
Future<void> loadHabits() async {
final loadedHabits = await storageService.loadHabits();
setState(() {
habits = loadedHabits;
isLoading = false;
});
}
Tambahkan function save:
Future<void> saveHabits() async {
await storageService.saveHabits(habits);
}
Setelah add habit:
setState(() {
habits.add(habit);
habitController.clear();
});
saveHabits();
Setelah toggle:
setState(() {
habits[index] = habit.copyWith(
isDoneToday: isDone,
lastCompletedAt: isDone ? DateTime.now() : null,
);
});
saveHabits();
Setelah delete:
setState(() {
habits.removeAt(index);
});
saveHabits();
Saat loading:
if (isLoading) {
return const Scaffold(
body: Center(
child: CircularProgressIndicator(),
),
);
}
20. Reset Status Harian
Habit Tracker seharusnya menandai habit selesai untuk hari tertentu.
Untuk versi sederhana, kita bisa reset isDoneToday jika lastCompletedAt bukan hari ini.
Tambahkan helper:
bool isSameDay(DateTime a, DateTime b) {
return a.year == b.year && a.month == b.month && a.day == b.day;
}
Saat load data:
Future<void> loadHabits() async {
final loadedHabits = await storageService.loadHabits();
final today = DateTime.now();
final normalizedHabits = loadedHabits.map((habit) {
final lastCompletedAt = habit.lastCompletedAt;
if (lastCompletedAt == null) {
return habit.copyWith(isDoneToday: false);
}
final doneToday = isSameDay(lastCompletedAt, today);
return habit.copyWith(isDoneToday: doneToday);
}).toList();
setState(() {
habits = normalizedHabits;
isLoading = false;
});
await storageService.saveHabits(normalizedHabits);
}
Penjelasan:
- app mengecek tanggal terakhir habit selesai
- jika tanggalnya bukan hari ini, status dikembalikan ke false
- ini membuat habit bisa dipakai lagi setiap hari
21. Menampilkan Progress
Tambahkan progress percent:
double get progress {
if (habits.isEmpty) return 0;
return completedCount / habits.length;
}
Tambahkan UI:
LinearProgressIndicator(value: progress),
const SizedBox(height: 8),
Text('${(progress * 100).round()}% selesai'),
Penjelasan:
- jika list kosong, progress
0 - jika 3 dari 5 habit selesai, progress
0.6 LinearProgressIndicatormenerima nilai 0 sampai 1
22. Full Flow Aplikasi
Alur lengkap:
App dibuka
-> initState
-> loadHabits dari SharedPreferences
-> reset status jika bukan hari ini
-> tampilkan list
User tambah habit
-> ambil text dari controller
-> validasi kosong
-> buat object Habit
-> masukkan ke list
-> saveHabits
User centang habit
-> toggle isDoneToday
-> isi lastCompletedAt
-> saveHabits
User hapus habit
-> remove dari list
-> saveHabits
Kalau kamu bisa menjelaskan flow ini, berarti pemahaman project sudah mulai kuat.
23. Prompt Review Kode
Setelah fitur berjalan, minta AI review:
Tolong review kode Habit Tracker saya.
Fokus review:
- apakah struktur file sudah rapi
- apakah state terlalu banyak di UI
- apakah local storage sudah aman untuk pemula
- apakah ada bug pada reset harian
- apakah ada memory leak
- apakah error handling perlu ditambah
- apa refactor kecil yang disarankan
Jangan ubah kode dulu.
Berikan review dan prioritas perbaikan.
Prompt ini membantu kamu belajar membaca kualitas kode.
24. Error yang Sering Muncul
Data tidak tersimpan
Penyebab:
- lupa memanggil
saveHabits toJsonsalah- key storage berbeda antara save dan load
Solusi:
- cek
_habitsKey - cek
saveHabitsdipanggil setelah add/toggle/delete - print string JSON untuk debugging
Error DateTime.parse
Penyebab:
- value tanggal null
- format tanggal tidak valid
Solusi:
- pastikan
createdAtselalu ada - cek
lastCompletedAtboleh null
UI tidak update
Penyebab:
- lupa
setState - perubahan list dilakukan di luar update UI
Solusi:
- pastikan perubahan list dibungkus
setState
Checkbox kembali false
Penyebab:
- reset harian menganggap tanggal bukan hari ini
- timezone atau data lama bermasalah
Solusi:
- cek
lastCompletedAt - cek function
isSameDay
25. Checklist Testing Manual
Gunakan checklist ini:
- aplikasi bisa dibuka
- empty state muncul saat belum ada habit
- user bisa tambah habit
- input kosong tidak menambah data
- setelah tambah, input kosong kembali
- habit muncul di list
- checkbox bisa dicentang
- progress berubah setelah checkbox dicentang
- habit bisa dihapus
- data tetap ada setelah app ditutup
- status selesai hari ini tetap tersimpan
- status reset saat hari berbeda
- tidak ada overflow di layar kecil
- loading muncul saat data dibaca
Testing tambahan:
- tambah 10 habit
- hapus habit pertama
- hapus habit terakhir
- centang semua habit
- hapus semua habit
- tutup dan buka aplikasi lagi
26. Catatan Belajar Setelah Selesai
Simpan catatan:
# Project Habit Tracker
## Fitur
- tambah habit
- list habit
- toggle selesai hari ini
- hapus habit
- progress harian
- local storage
## Konsep Flutter
- StatefulWidget
- setState
- TextEditingController
- ListView
- Checkbox
- LinearProgressIndicator
## Konsep Dart
- class
- List
- DateTime
- toJson
- fromJson
- copyWith
## Error yang Dipelajari
- lupa setState membuat UI tidak update
- lupa save membuat data hilang
- DateTime perlu diubah ke string saat disimpan
## Next
- tambah fitur edit habit
- tambah kategori habit
- refactor ke Provider
Dokumentasi ini akan berguna saat kamu lupa atau ingin membuat project serupa.
27. Challenge Pengembangan
Setelah MVP selesai, coba tambah fitur:
Challenge 1: Edit Habit
Fitur:
- tap habit
- buka dialog edit
- ubah title
- simpan perubahan
Prompt:
Tambahkan fitur edit habit ke Habit Tracker saya.
Gunakan AlertDialog dengan TextField.
Setelah simpan, update list dan save ke local storage.
Jelaskan perubahan kode.
Challenge 2: Kategori Habit
Fitur:
- habit punya kategori
- contoh kategori: Health, Study, Work
- tampilkan kategori di subtitle
Challenge 3: Filter Habit
Fitur:
- semua
- selesai
- belum selesai
Challenge 4: Statistik Mingguan
Fitur:
- simpan history tanggal selesai
- tampilkan jumlah selesai 7 hari terakhir
Untuk challenge 4, model perlu dikembangkan lagi.
28. Refactor ke Provider
Setelah versi StatefulWidget paham, lanjut refactor ke Provider.
Target struktur:
lib/
models/
habit.dart
services/
habit_storage_service.dart
providers/
habit_provider.dart
pages/
habit_page.dart
Prompt:
Saya sudah punya Habit Tracker dengan StatefulWidget.
Bantu refactor ke Provider.
Target:
- HabitProvider menyimpan list habit
- HabitProvider punya loadHabits, addHabit, toggleHabit, deleteHabit
- HabitPage hanya fokus UI
- HabitStorageService tetap dipakai
- jelaskan alur UI -> Provider -> Storage
Refactor ini penting agar project lebih siap berkembang.
29. Upgrade ke Firebase
Setelah local storage aman, project bisa di-upgrade ke Firebase.
Target:
- user login
- habit tersimpan di Firestore
- habit punya
userId - data per user
- realtime update
- security rules
Prompt:
Saya punya Habit Tracker lokal dengan Provider dan local storage.
Bantu buat roadmap upgrade ke Firebase.
Target:
- Firebase Auth
- Firestore collection habits
- HabitRepository
- data per user
- Security Rules
- migrasi bertahap tanpa langsung menghapus versi local storage
Untuk tahap awal, jangan langsung membuat sync offline-online. Mulai dari Firestore online dulu.
30. Kesimpulan
Project Habit Tracker adalah latihan bagus untuk belajar Flutter Mobile dengan vibe coding.
Yang dipelajari:
- membuat project dari ide
- memecah fitur menjadi tahap kecil
- membuat prompt yang jelas
- membangun model data
- membuat UI list
- mengelola state lokal
- menyimpan data dengan local storage
- membaca error
- melakukan testing manual
- membuat dokumentasi belajar
Pola penting:
Prompt kecil
-> kode kecil
-> run
-> debug
-> pahami
-> catat
-> lanjut
Dengan pola ini, vibe coding menjadi cara belajar yang produktif, bukan sekadar copy-paste.
31. Selanjutnya
Materi berikutnya yang cocok adalah Project Flutter Mobile Vibe Coding: Expense Tracker.
Di project itu kita akan belajar data transaksi, kategori, total saldo, filter bulan, dan chart sederhana.