State Management Flutter di 2026: Riverpod vs Bloc vs GetX — Panduan Lengkap
Perbandingan praktis solusi state management Flutter di tahun 2026. Riverpod 3.0, Bloc 9.0, dan GetX dievaluasi dengan contoh kode nyata, analisis performa, dan strategi migrasi.

State management pada Flutter menentukan cara aplikasi mengelola aliran data antar widget. Di tahun 2026, tiga solusi mendominasi ekosistem: Riverpod 3.0 dengan keamanan compile-time, Bloc 9.0 dengan kemampuan pelacakan event tingkat enterprise, dan GetX yang penggunaannya semakin menurun namun masih hadir di berbagai proyek. Pemilihan solusi yang tepat berdampak langsung pada testability, skalabilitas, dan biaya pemeliharaan jangka panjang.
Riverpod 3.0 cocok untuk sebagian besar proyek berkat keamanan compile-time dan boilerplate yang minimal. Bloc 9.0 tetap menjadi standar untuk industri teregulasi yang membutuhkan audit trail berbasis event. GetX sebaiknya hanya dipertimbangkan untuk memelihara codebase yang sudah ada tanpa anggaran migrasi.
Riverpod 3.0: Keamanan Compile-Time dan Auto-Retry
Riverpod 3.0 memperkenalkan perubahan mendasar dalam cara aplikasi Flutter mendeklarasikan dan mengonsumsi state. Code generation berbasis anotasi menangkap kesalahan dependensi pada saat kompilasi, bukan saat runtime, sehingga menghilangkan seluruh kategori bug yang sebelumnya memerlukan pengujian manual.
Mekanisme auto-retry untuk provider yang gagal menangani error jaringan sementara tanpa intervensi manual. Ketika komputasi provider gagal, Riverpod secara otomatis mencoba ulang dengan delay yang dapat dikonfigurasi, mengurangi kode boilerplate untuk pemulihan error.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'counter_provider.g.dart';
// Code generation ensures compile-time safety
class Counter extends _$Counter {
int build() => 0; // Initial state
void increment() => state = state + 1;
void decrement() => state = state - 1;
void reset() => state = 0;
}Anotasi @riverpod menghasilkan semua boilerplate provider secara otomatis. Ketidakcocokan tipe, override yang hilang, dan dependensi sirkuler terdeteksi selama proses kompilasi.
import 'package:riverpod_annotation/riverpod_annotation.dart';
part 'user_repository_provider.g.dart';
Future<User> currentUser(Ref ref) async {
final authService = ref.watch(authServiceProvider);
final userId = authService.currentUserId;
// Auto-retry on network failure (Riverpod 3.0)
final response = await ref.watch(
httpClientProvider,
).get('/api/users/$userId');
return User.fromJson(response.data);
}Riverpod 3.0 juga secara otomatis menjeda listener provider ketika widget meninggalkan layar, mengurangi komputasi yang tidak perlu dan meningkatkan efisiensi baterai pada perangkat mobile.
Bloc 9.0: Arsitektur Event-Driven untuk Aplikasi Enterprise
Bloc 9.0 menerapkan pemisahan ketat antara event, state, dan business logic. Setiap perubahan state dipetakan ke event tertentu, menciptakan audit trail yang dibutuhkan oleh industri teregulasi. Pemeriksaan keamanan mounted dalam versi 9.0 mencegah callback dieksekusi pada widget yang sudah di-dispose.
sealed class AuthenticationEvent {}
final class LoginRequested extends AuthenticationEvent {
final String email;
final String password;
LoginRequested({required this.email, required this.password});
}
final class LogoutRequested extends AuthenticationEvent {}
final class SessionRestored extends AuthenticationEvent {
final String token;
SessionRestored({required this.token});
}Sealed class pada Dart 3 menjamin exhaustive pattern matching pada event. Compiler memastikan bahwa setiap tipe event memiliki handler yang sesuai.
import 'package:flutter_bloc/flutter_bloc.dart';
class AuthenticationBloc
extends Bloc<AuthenticationEvent, AuthenticationState> {
final AuthRepository _authRepo;
final TokenStorage _tokenStorage;
AuthenticationBloc({
required AuthRepository authRepo,
required TokenStorage tokenStorage,
}) : _authRepo = authRepo,
_tokenStorage = tokenStorage,
super(AuthenticationInitial()) {
on<LoginRequested>(_onLoginRequested);
on<LogoutRequested>(_onLogoutRequested);
on<SessionRestored>(_onSessionRestored);
}
Future<void> _onLoginRequested(
LoginRequested event,
Emitter<AuthenticationState> emit,
) async {
emit(AuthenticationLoading());
try {
final token = await _authRepo.login(
email: event.email,
password: event.password,
);
await _tokenStorage.save(token);
emit(AuthenticationSuccess(token: token));
} catch (e) {
emit(AuthenticationFailure(message: e.toString()));
}
}
Future<void> _onLogoutRequested(
LogoutRequested event,
Emitter<AuthenticationState> emit,
) async {
await _tokenStorage.clear();
emit(AuthenticationInitial());
}
Future<void> _onSessionRestored(
SessionRestored event,
Emitter<AuthenticationState> emit,
) async {
emit(AuthenticationSuccess(token: event.token));
}
}Setiap event handler menghasilkan transisi state yang jelas. Logging middleware dapat mencatat setiap event untuk keperluan debugging atau kepatuhan regulasi. Interface EmittableStateStreamableSource pada Bloc 9.0 menyederhanakan pengujian dengan memungkinkan implementasi mock yang ringan.
Event Transformer Bloc: Menangani Input Frekuensi Tinggi
Bloc menyediakan event transformer bawaan yang menyelesaikan masalah concurrency umum. Pencarian saat mengetik, ketukan tombol berulang, dan stream data real-time semuanya mendapat manfaat dari pemrosesan event secara deklaratif.
import 'package:bloc_concurrency/bloc_concurrency.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
class SearchBloc extends Bloc<SearchEvent, SearchState> {
final SearchRepository _repository;
SearchBloc({required SearchRepository repository})
: _repository = repository,
super(SearchInitial()) {
// restartable() cancels previous search on new input
on<SearchQueryChanged>(
_onQueryChanged,
transformer: restartable(),
);
// droppable() ignores events while processing
on<SearchResultSelected>(
_onResultSelected,
transformer: droppable(),
);
}
Future<void> _onQueryChanged(
SearchQueryChanged event,
Emitter<SearchState> emit,
) async {
if (event.query.length < 3) {
emit(SearchInitial());
return;
}
emit(SearchLoading());
final results = await _repository.search(event.query);
emit(SearchLoaded(results: results));
}
Future<void> _onResultSelected(
SearchResultSelected event,
Emitter<SearchState> emit,
) async {
emit(SearchNavigating(result: event.result));
}
}Transformer restartable() membatalkan pencarian yang sedang berjalan ketika input baru tiba, mencegah hasil yang sudah usang menimpa hasil yang lebih baru. Transformer droppable() mengabaikan ketukan duplikat selama navigasi sedang berlangsung.
Siap menguasai wawancara Flutter Anda?
Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.
GetX: Utang Teknis dan Realita Migrasi
GetX mendapatkan popularitas melalui kecepatan prototyping dan boilerplate yang minimal. Di tahun 2026, library ini menghadapi krisis pemeliharaan: pembaruan yang sporadis, ketergantungan pada satu maintainer, dan inkompatibilitas yang terus bertambah dengan versi terbaru Flutter SDK. Aplikasi produksi yang menggunakan GetX mengalami masalah lifecycle controller dan kebocoran memori akibat singleton global yang implisit.
import 'package:get/get.dart';
// Global singleton - difficult to test and scope
class CounterController extends GetxController {
final count = 0.obs; // Reactive observable
void increment() => count.value++;
void decrement() => count.value--;
// Lifecycle hooks - disposal timing is unpredictable
void onClose() {
// Cleanup may not execute reliably
super.onClose();
}
}
// Usage in widget
class CounterPage extends StatelessWidget {
Widget build(BuildContext context) {
// Get.put creates a global singleton
final controller = Get.put(CounterController());
return Obx(() => Text('${controller.count}'));
}
}Pemanggilan Get.put() mendaftarkan controller sebagai singleton global. Dalam alur navigasi yang kompleks, controller bertahan melampaui cakupan yang dimaksudkan, sehingga mengonsumsi memori. Variabel reaktif .obs melewati sistem notifikasi state standar Flutter, membuat integrasi dengan paket lain menjadi tidak andal.
Migrasi dari GetX ke Riverpod: Langkah demi Langkah
Bagi tim yang memelihara codebase GetX, migrasi ke Riverpod dapat dilakukan secara bertahap. Kedua library dapat berdampingan dalam proyek yang sama, memungkinkan konversi layar per layar tanpa perlu menulis ulang seluruh aplikasi.
// Step 1: Replace GetX controller with Riverpod notifier
// Before (GetX)
class ProductController extends GetxController {
final products = <Product>[].obs;
final isLoading = false.obs;
Future<void> loadProducts() async {
isLoading.value = true;
products.value = await ProductApi.fetchAll();
isLoading.value = false;
}
}
// After (Riverpod 3.0)
class ProductList extends _$ProductList {
Future<List<Product>> build() async {
// Auto-retry on failure, auto-pause when off-screen
return ProductApi.fetchAll();
}
Future<void> refresh() async {
ref.invalidateSelf();
}
}// Step 2: Replace widget bindings
// Before (GetX)
class ProductPage extends StatelessWidget {
Widget build(BuildContext context) {
final ctrl = Get.put(ProductController());
return Obx(() {
if (ctrl.isLoading.value) return CircularProgressIndicator();
return ListView.builder(
itemCount: ctrl.products.length,
itemBuilder: (_, i) => ProductTile(ctrl.products[i]),
);
});
}
}
// After (Riverpod 3.0)
class ProductPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final productsAsync = ref.watch(productListProvider);
return productsAsync.when(
loading: () => const CircularProgressIndicator(),
error: (err, stack) => ErrorDisplay(error: err),
data: (products) => ListView.builder(
itemCount: products.length,
itemBuilder: (_, i) => ProductTile(products[i]),
),
);
}
}Versi Riverpod menangani state loading, error, dan data secara eksplisit melalui AsyncValue.when(). Tidak ada singleton global, tidak ada manajemen lifecycle manual, dan disposal otomatis ketika widget di-unmount.
Perbandingan Performa: Efisiensi Rebuild
Efisiensi rebuild berdampak langsung pada frame rate. Setiap solusi menangani rebuild widget secara berbeda, dan perbedaannya menjadi terukur pada daftar dengan ratusan item.
| Metrik | Riverpod 3.0 | Bloc 9.0 | GetX |
|--------|-------------|----------|------|
| Rebuild selektif | select() filter | BlocSelector | .obs per field |
| Keamanan compile-time | Penuh (code gen) | Parsial (sealed classes) | Tidak ada |
| Auto-dispose | Bawaan | Manual via close() | Tidak andal |
| Jeda saat off-screen | Otomatis (3.0) | Manual | Tidak didukung |
| Pelacakan event | Provider observer | Log event lengkap | Tidak ada |
| Isolasi pengujian | ProviderContainer.test() | EmittableStateStreamableSource | Memerlukan Get.testMode |
| Dampak ukuran bundle | ~45KB | ~38KB | ~120KB (termasuk routing, DI, HTTP) |
Metode select() Riverpod dan BlocSelector Bloc sama-sama memungkinkan rebuild yang presisi, hanya memperbarui subtree widget yang bergantung pada data yang berubah. .obs GetX mencapai granularitas serupa per field tetapi tidak memiliki verifikasi compile-time terhadap grafik dependensi.
GetX menggabungkan routing, dependency injection, HTTP client, dan state management dalam satu paket. Aplikasi yang hanya menggunakan state management tetap mengimpor seluruh library sebesar 120KB. Riverpod dan Bloc adalah paket terfokus yang melakukan satu hal dengan baik.
Strategi Pengujian untuk Setiap Solusi
Testability sering menentukan solusi mana yang dapat berkembang seiring pertumbuhan tim. Setiap library memiliki pendekatan pengujian yang berbeda.
// Riverpod test - isolated container
import 'package:flutter_test/flutter_test.dart';
import 'package:riverpod/riverpod.dart';
void main() {
test('Counter increments', () {
final container = ProviderContainer.test();
// Override dependencies for isolation
final counter = container.read(counterProvider.notifier);
expect(container.read(counterProvider), 0);
counter.increment();
expect(container.read(counterProvider), 1);
});
}// Bloc test - event-driven verification
import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
blocTest<AuthenticationBloc, AuthenticationState>(
'emits [loading, success] on valid login',
build: () => AuthenticationBloc(
authRepo: MockAuthRepo(),
tokenStorage: MockTokenStorage(),
),
act: (bloc) => bloc.add(
LoginRequested(email: 'dev@test.com', password: 'secure123'),
),
expect: () => [
isA<AuthenticationLoading>(),
isA<AuthenticationSuccess>(),
],
);
}ProviderContainer.test() dari Riverpod membuat grafik dependensi yang terisolasi per pengujian. Helper blocTest dari Bloc memverifikasi urutan transisi state secara tepat, sesuai dengan arsitektur event-driven. Pengujian GetX memerlukan pengaturan Get.testMode = true dan pengelolaan lifecycle controller secara manual, yang sering menyebabkan pengujian yang tidak stabil di lingkungan CI.
State management Flutter termasuk topik yang paling sering ditanyakan dalam wawancara developer mobile. Memahami trade-off antara Riverpod, Bloc, dan GetX menunjukkan kematangan arsitektural. Berlatihlah menjelaskan kapan setiap solusi cocok dan kapan tidak cocok digunakan.
Matriks Keputusan: Memilih Solusi yang Tepat
Kendala proyek menentukan pilihan terbaik. Ukuran tim, persyaratan regulasi, dan codebase yang sudah ada semuanya menjadi faktor dalam pengambilan keputusan.
Riverpod 3.0 cocok ketika tim mengutamakan keamanan compile-time, proyek membutuhkan pengambilan data async dengan pemulihan error otomatis, atau codebase dibangun dari awal. Kurva pembelajaran bersifat moderat: developer yang sudah familiar dengan Provider dapat bertransisi secara alami.
Bloc 9.0 cocok ketika proyek beroperasi di industri teregulasi (fintech, kesehatan), tim membutuhkan pelacakan event lengkap untuk audit, atau aplikasi menangani alur kerja concurrent yang kompleks seperti pemrosesan pembayaran. Biaya boilerplate terbayar dengan kemudahan pemeliharaan dalam skala besar.
GetX hanya cocok untuk memelihara codebase GetX yang sudah ada di mana biaya migrasi melebihi anggaran yang tersedia. Memulai proyek baru dengan GetX di tahun 2026 berarti menciptakan utang teknis sejak hari pertama. Dokumentasi resmi Flutter tidak mencantumkan GetX di antara solusi yang direkomendasikan.
Untuk latihan lebih mendalam tentang pola state management Flutter, modul dasar-dasar state management Flutter membahas konsep-konsep fundamental yang diuji dalam wawancara. Modul pola provider mengeksplorasi strategi dependency injection yang berlaku untuk ketiga solusi.
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Kesimpulan
- Riverpod 3.0 menyediakan keamanan compile-time melalui code generation, retry otomatis untuk provider yang gagal, dan dukungan pause/resume yang mengurangi konsumsi baterai pada perangkat mobile
- Bloc 9.0 menerapkan transisi state berbasis event dengan kemampuan audit penuh, menjadikannya standar untuk aplikasi enterprise di industri teregulasi
- GetX menghadapi krisis pemeliharaan di tahun 2026 dengan pembaruan yang sporadis dan inkompatibilitas SDK yang semakin bertambah; proyek GetX yang sudah ada perlu merencanakan migrasi bertahap ke Riverpod
- Migrasi dari GetX ke Riverpod dilakukan layar per layar tanpa perlu menulis ulang seluruh aplikasi, karena kedua library dapat berdampingan dalam proyek yang sama
- Isolasi pengujian berbeda secara signifikan: Riverpod menggunakan
ProviderContainer.test(), Bloc menggunakanblocTestdengan verifikasi urutan event, dan GetX memerlukan konfigurasi test mode global yang rentan - Ukuran bundle penting pada mobile: Riverpod (~45KB) dan Bloc (~38KB) menyediakan paket yang terfokus, sementara GetX (~120KB) membawa fitur-fitur yang tidak terpakai
Mulai berlatih!
Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.
Tag
Bagikan
Artikel terkait

State Management Flutter: Riverpod vs BLoC - Panduan Perbandingan Lengkap
Perbandingan mendalam antara Riverpod dan BLoC untuk state management di Flutter. Arsitektur, performa, kemudahan pengujian, dan kasus penggunaan untuk memilih solusi terbaik.

20 Pertanyaan Wawancara Flutter Teratas untuk Developer Mobile
Persiapkan wawancara Flutter dengan 20 pertanyaan yang paling sering ditanyakan. Widget, state management, Dart, arsitektur, dan praktik terbaik dijelaskan secara detail dengan contoh kode.

Flutter: Membangun Aplikasi Lintas Platform Pertama
Panduan lengkap untuk membangun aplikasi mobile lintas platform dengan Flutter dan Dart. Widget, manajemen state, navigasi, dan praktik terbaik untuk pemula.