✦ Selamat Idul Fitri 1447 H πŸŒ™ Taqabbalallahu minna wa minkum. Mohon maaf lahir dan batin. ✦
Coding

State Management dan Penyimpanan Lokal di Flutter: Panduan Praktis

Β· 0 komentar Β· Β± 4 menit baca Β· πŸ‘ 119 dilihat

State Management dan Penyimpanan Lokal di Flutter: Panduan Praktis

Dua pertanyaan yang paling sering muncul dari developer Flutter yang baru melewati tahap "hello world" adalah: bagaimana cara mengelola state yang kompleks, dan bagaimana cara menyimpan data agar tidak hilang ketika aplikasi ditutup. Dua pertanyaan yang berbeda tapi saling berkaitan erat dalam praktiknya.

Dari beberapa aplikasi yang sudah saya buat, ada pola yang akhirnya saya gunakan secara konsisten. Bukan yang paling canggih, tapi yang paling seimbang antara kesederhanaan dan kemampuan.

Kapan setState Sudah Cukup

setState itu bukan pilihan yang naif β€” ini adalah solusi yang tepat untuk kasus yang tepat. Kalau state hanya relevan di dalam satu widget dan tidak perlu dibagi ke widget lain, setState adalah pilihan terbaik: simpel, mudah dibaca, tanpa dependency tambahan.

class FormPencarian extends StatefulWidget {
const FormPencarian({super.key});
@override
State<FormPencarian> createState() => _FormPencarianState();
}
class _FormPencarianState extends State<FormPencarian> {
final _controller = TextEditingController();
List<String> _hasil = [];
bool _isLoading = false;
Future<void> _cari() async {
if (_controller.text.trim().isEmpty) return;
setState(() => _isLoading = true);
// Simulasi pencarian
await Future.delayed(const Duration(seconds: 1));
final hasil = ["Hasil 1", "Hasil 2", "Hasil 3"];
setState(() {
_hasil = hasil;
_isLoading = false;
});
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(children: [
TextField(controller: _controller, onSubmitted: (_) => _cari()),
if (_isLoading) const CircularProgressIndicator(),
...(_hasil.map((h) => ListTile(title: Text(h)))),
]);
}
}

Provider untuk State yang Perlu Dibagi

Begitu state perlu diakses dari beberapa widget yang berbeda β€” misalnya status login yang harus tercermin di AppBar, drawer, dan halaman profil sekaligus β€” setState mulai tidak memadai. Di sinilah Provider mulai berperan.

// 1. Definisikan model
class AuthModel extends ChangeNotifier {
String? _namaUser;
bool get isLoggedIn => _namaUser != null;
String? get namaUser => _namaUser;
Future<void> login(String nama, String password) async {
// Proses autentikasi...
_namaUser = nama;
notifyListeners();
}
void logout() {
_namaUser = null;
notifyListeners();
}
}
// 2. Daftarkan di root aplikasi
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => AuthModel(),
child: const MyApp(),
),
);
}
// 3. Akses dari widget manapun
class TombolProfil extends StatelessWidget {
@override
Widget build(BuildContext context) {
final auth = context.watch<AuthModel>();
return auth.isLoggedIn
? Text("Halo, ${auth.namaUser}!")
: const Text("Belum login");
}
}

SharedPreferences untuk Data Sederhana

SharedPreferences cocok untuk menyimpan preferensi pengguna: tema gelap/terang, bahasa, status onboarding, atau setting kecil lainnya. Data tersimpan permanen sampai aplikasi di-uninstall.

class PreferenceService {
static const _keyTema    = "tema_gelap";
static const _keyBahasa = "bahasa";
static Future<SharedPreferences> get _prefs => SharedPreferences.getInstance();
static Future<bool> getTemaGelap() async {
return (await _prefs).getBool(_keyTema) ?? false;
}
static Future<void> setTemaGelap(bool aktif) async {
await (await _prefs).setBool(_keyTema, aktif);
}
static Future<String> getBahasa() async {
return (await _prefs).getString(_keyBahasa) ?? "id";
}
}

Hive untuk Data yang Lebih Kompleks

Kalau butuh menyimpan koleksi objek yang bisa di-CRUD β€” misalnya daftar bookmark, catatan, atau history β€” Hive adalah pilihan yang lebih tepat dari SharedPreferences.

// Setup di main()
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Hive.initFlutter();
await Hive.openBox("bookmarks");
runApp(const MyApp());
}
// Service untuk bookmark
class BookmarkService {
static Box get _box => Hive.box("bookmarks");
static List<Map> getAll() {
return _box.values.cast<Map>().toList();
}
static Future<void> tambah(String id, String judul) async {
await _box.put(id, {
"id": id,
"judul": judul,
"disimpan_pada": DateTime.now().toIso8601String(),
});
}
static bool isSaved(String id) => _box.containsKey(id);
static Future<void> hapus(String id) async => await _box.delete(id);
static Future<void> toggle(String id, String judul) async {
isSaved(id) ? await hapus(id) : await tambah(id, judul);
}
}
// Widget bookmark button
class TombolBookmark extends StatefulWidget {
final String id;
final String judul;
const TombolBookmark({super.key, required this.id, required this.judul});
@override
State<TombolBookmark> createState() => _TombolBookmarkState();
}
class _TombolBookmarkState extends State<TombolBookmark> {
late bool _isSaved;
@override
void initState() {
super.initState();
_isSaved = BookmarkService.isSaved(widget.id);
}
Future<void> _toggle() async {
await BookmarkService.toggle(widget.id, widget.judul);
setState(() => _isSaved = !_isSaved);
}
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(_isSaved ? Icons.bookmark : Icons.bookmark_outline),
color: _isSaved ? Colors.amber : null,
onPressed: _toggle,
);
}
}

Kombinasi Provider + Hive

Pola yang sering saya pakai untuk fitur yang lebih kompleks: Hive untuk persistensi data, Provider untuk expose data tersebut ke widget tree:

class BookmarkModel extends ChangeNotifier {
List<Map> _bookmarks = [];
List<Map> get bookmarks => _bookmarks;
BookmarkModel() {
_muat();
}
void _muat() {
_bookmarks = BookmarkService.getAll();
notifyListeners();
}
Future<void> toggle(String id, String judul) async {
await BookmarkService.toggle(id, judul);
_muat();
}
bool isSaved(String id) => BookmarkService.isSaved(id);
}

Penutup

State management dan local storage itu dua puzzle yang perlu diselesaikan di hampir setiap aplikasi Flutter. Tidak perlu langsung pakai solusi paling canggih β€” mulai dari setState, tambahkan Provider saat dibutuhkan, dan pilih storage sesuai kompleksitas data yang disimpan.

Yang terpenting adalah memahami kapan masing-masing tool tepat dipakai, bukan sekadar mengikuti trend library yang sedang populer. πŸ™‚


Komentar

Belum ada komentar. Jadilah yang pertama menulis.

Tulis Komentar

↑