Mengelola state aplikasi skala menengah: konsep local vs global state, perbandingan Zustand/Redux untuk React Native, Provider/Riverpod/Bloc untuk Flutter, dan StateFlow/ObservableObject untuk native — plus memilih yang tepat untuk Fitku

Di episode 8 kita mengatur struktur file — kini kita mengatur data yang mengalir di dalamnya: state management. Setiap aplikasi punya data yang dibaca banyak layar sekaligus: profil pengguna, langkah hari ini, daftar workout. Bagaimana menyimpannya agar semua layar konsisten, dan bagaimana perubahannya memicu update UI?
State management adalah topik yang paling sering memicu perdebatan di tim mobile. Jawaban yang benar selalu kontekstual — episode ini memberi kalian kerangka memilih, bukan resep tunggal.
Pertama, bedakan dua jenis state:
useState/@State/remember.Profil user → global (dipakai di semua layar)
Langkah hari ini → global (widget, progress, beranda)
Pencarian workout → local (hanya di layar pencarian)
Filter list → local (hanya di layar list)Aturan praktis: jika state hanya dipakai satu layar, jangan global-kan — menyimpan terlalu banyak di store membuat aplikasi sulit diprediksi.
Zustand adalah pilihan modern yang ringkas — store dibuat dengan hook, tidak butuh Provider:
import { create } from "zustand";
type FitkuStore = {
steps: number;
addSteps: (n: number) => void;
reset: () => void;
};
export const useFitkuStore = create<FitkuStore>((set) => ({
steps: 0,
addSteps: (n) => set((s) => ({ steps: s.steps + n })),
reset: () => set({ steps: 0 }),
}));Konsumsi di komponen mana pun tanpa Provider:
function HomeScreen() {
const steps = useFitkuStore((s) => s.steps);
const addSteps = useFitkuStore((s) => s.addSteps);
return (
<View>
<Text>{steps}</Text>
<Button title="Tambah" onPress={() => addSteps(1)} />
</View>
);
}Redux lebih berat tetapi sangat eksplisit dan punya DevTools matang — cocok untuk aplikasi besar dengan banyak event terstruktur. Redux Toolkit (RTK) menyederhanakan boilerplate:
import { createSlice, configureStore } from "@reduxjs/toolkit";
const fitkuSlice = createSlice({
name: "fitku",
initialState: { steps: 0 },
reducers: {
added: (s, a) => { s.steps += a.payload; },
},
});
export const store = configureStore({ reducer: fitkuSlice.reducer });Pilih Zustand untuk fleksibilitas & sedikit kode; Redux jika butuh determinisme, tooling, dan tim besar. Zustand lebih populer untuk proyek baru 2026.
Provider adalah solusi resmi-sederhana; Riverpod adalah suksesornya yang lebih aman (compile-time safe, tanpa BuildContext):
final stepsProvider = StateProvider<int>((ref) => 0);
final stepsCountProvider = Provider((ref) {
return ref.watch(stepsProvider);
});
class HomeScreen extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final steps = ref.watch(stepsCountProvider);
return Column(
children: [
Text('$steps'),
FilledButton(
onPressed: () => ref.read(stepsProvider.notifier).state++,
child: const Text('Tambah langkah'),
),
],
);
}
}Bloc menerjemahkan arsitektur MVI dari episode 8: events masuk, states keluar.
sealed class StepEvent {}
final class StepAdded extends StepEvent {}
class StepBloc extends Bloc<StepEvent, int> {
StepBloc() : super(0) {
on<StepAdded>((event, emit) => emit(state + 1));
}
}Pilih Provider/Riverpod untuk kesederhanaan; Bloc untuk proyek dengan alur kompleks dan standar tim yang kuat.
Note
Pilihan tool kurang penting daripada disiplin: satu sumber kebenaran, update deterministik, dan pisahkan state dari UI. Tim yang bagus bisa menghasilkan app rapi memakai setState; tim ceroboh akan berantakan meski pakai Redux.
Pola bawaan Android modern: StateFlow di ViewModel + collectAsState() di Compose — persis contoh episode 3 dan 8. Untuk state yang dipertahankan saat konfigurasi berubah, ViewModel menahannya.
Di SwiftUI, padanannya @Observable / ObservableObject + @StateObject:
@MainActor
final class FitkuStore: ObservableObject {
@Published var steps = 0
func addSteps(_ n: Int) { steps += n }
}
struct HomeView: View {
@StateObject private var store = FitkuStore()
var body: some View {
VStack {
Text("\(store.steps)")
Button("Tambah") { store.addSteps(1) }
}
}
}Skala menengah Fitku cukup dengan: Zustand (RN) atau Riverpod (Flutter) untuk global state, StateFlow (Android) dan ObservableObject (iOS) untuk state per-layar, plus repository dari episode 8 sebagai sumber data. Simpan di store hanya yang benar-benar dibagikan antar layar.
Tip
Kuasai satu tool dulu secara dalam (misal Zustand atau Riverpod) sebelum bereksperimen dengan yang lain. Kemampuan berpindah tool jauh lebih mudah setelah kalian memahami konsep: derived state, selectors, dan satu sumber kebenaran.
Pada episode 9 ini, kalian telah menguasai peta state management:
Di episode 10 selanjutnya kita menghubungkan aplikasi ke dunia luar: Networking & API Integration — REST/GraphQL client, error handling, retry, dan caching untuk aplikasi Fitku. Sampai jumpa!