Quản lý State trong Flutter năm 2026: Riverpod vs Bloc vs GetX
So sánh thực tế các giải pháp quản lý state trong Flutter năm 2026. Riverpod 3.4, Bloc 9.1 và GetX được đánh giá qua ví dụ code thực tế, phân tích hiệu năng và chiến lược migration.

Quản lý state trong Flutter xác định cách ứng dụng xử lý luồng dữ liệu giữa các widget. Trong năm 2026, ba giải pháp chiếm lĩnh hệ sinh thái: Riverpod 3.4 với tính an toàn compile-time và các extension provider mới, Bloc 9.1 với khả năng truy vết event cấp doanh nghiệp, và GetX với sự hiện diện đang dần suy giảm. Việc lựa chọn đúng giải pháp ảnh hưởng trực tiếp đến khả năng kiểm thử, khả năng mở rộng và chi phí bảo trì dài hạn.
Riverpod 3.4 phù hợp với đa số dự án nhờ tính an toàn compile-time, boilerplate tối thiểu, và tích hợp ValueListenable mới. Bloc 9.1 vẫn là tiêu chuẩn cho các ngành công nghiệp được quản lý chặt chẽ cần audit trail dựa trên event. GetX chỉ nên được cân nhắc khi bảo trì codebase hiện có mà không có ngân sách cho migration.
Riverpod 3.4: An toàn Compile-Time, Auto-Retry và ValueListenable
Riverpod 3.4 đã mang đến sự thay đổi căn bản trong cách ứng dụng Flutter khai báo và sử dụng state. Code generation dựa trên annotation phát hiện lỗi dependency tại thời điểm biên dịch thay vì runtime, loại bỏ hoàn toàn một nhóm bug trước đây cần kiểm thử thủ công mới phát hiện được.
Cơ chế auto-retry cho các provider bị lỗi xử lý các lỗi mạng tạm thời mà không cần can thiệp thủ công. Khi tính toán của provider thất bại, Riverpod tự động thử lại với thời gian chờ có thể cấu hình, giảm đáng kể code boilerplate cho việc phục hồi lỗi.
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;
}Annotation @riverpod tự động sinh toàn bộ boilerplate cho provider. Sự không khớp kiểu dữ liệu, thiếu override và dependency vòng tròn đều được phát hiện trong quá trình biên dịch.
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
final response = await ref.watch(
httpClientProvider,
).get('/api/users/$userId');
return User.fromJson(response.data);
}Riverpod 3.4 cũng tự động tạm dừng các listener của provider khi widget rời khỏi màn hình, giảm các tính toán không cần thiết và cải thiện thời lượng pin trên thiết bị di động.
Tính năng mới trong Riverpod 3.4: ValueListenable và Provider Extension
Riverpod 3.4 bổ sung CustomProviderListenable để xây dựng các extension provider tùy chỉnh và tích hợp ValueListenable. Thuộc tính listenable expose provider cho code yêu cầu interface ValueListenable native của Flutter, cho phép tích hợp mượt mà hơn với animation controller và các widget legacy.
class AnimatedCounter extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
// Expose provider as ValueListenable for animation integration
final counterListenable = ref.watch(counterProvider.listenable);
return ValueListenableBuilder<int>(
valueListenable: counterListenable,
builder: (context, count, child) {
return AnimatedSwitcher(
duration: const Duration(milliseconds: 300),
child: Text(
'$count',
key: ValueKey(count),
style: Theme.of(context).textTheme.headlineLarge,
),
);
},
);
}
}Phương thức ProviderContainer.allProviders() liệt kê tất cả các provider đang hoạt động, hữu ích cho việc debugging và xây dựng developer tools. Điều này giải quyết một vấn đề phổ biến khi theo dõi các state provider không mong muốn trong các ứng dụng phức tạp.
void debugPrintActiveProviders(ProviderContainer container) {
final providers = container.allProviders();
for (final provider in providers) {
debugPrint('Active: ${provider.name ?? provider.runtimeType}');
}
}Bloc 9.1: Kiến trúc Event-Driven cho ứng dụng doanh nghiệp
Bloc 9.1 áp dụng sự phân tách nghiêm ngặt giữa event, state và business logic. Mỗi thay đổi state được ánh xạ tới một event cụ thể, tạo ra audit trail mà các ngành công nghiệp được quản lý yêu cầu. Các kiểm tra an toàn mounted ngăn chặn callback thực thi trên các widget đã bị 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 trong Dart 3 đảm bảo exhaustive pattern matching trên các event. Trình biên dịch buộc mỗi loại event phải có handler tương ứng.
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));
}
}Mỗi event handler tạo ra một chuyển đổi state rõ ràng. Logging middleware có thể ghi lại mọi event phục vụ mục đích debug hoặc tuân thủ quy định. Bloc 9.1 bổ sung khả năng expose dispose callback trên RepositoryProvider, đơn giản hóa việc dọn dẹp resource trong các đồ thị dependency phức tạp.
Event Transformer trong Bloc: Xử lý đầu vào tần suất cao
Bloc cung cấp các event transformer tích hợp sẵn giải quyết các vấn đề concurrency phổ biến. Tìm kiếm khi đang gõ, nhấn nút liên tục và stream dữ liệu thời gian thực đều được hưởng lợi từ xử lý event khai báo.
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() hủy bỏ bất kỳ tìm kiếm đang thực hiện nào khi có đầu vào mới, ngăn kết quả cũ ghi đè lên kết quả mới. Transformer droppable() bỏ qua các lần nhấn trùng lặp khi quá trình điều hướng đang diễn ra.
Sẵn sàng chinh phục phỏng vấn Flutter?
Luyện tập với mô phỏng tương tác, flashcards và bài kiểm tra kỹ thuật.
GetX: Nợ kỹ thuật và thực tế Migration
GetX đạt được sự phổ biến nhờ tốc độ prototyping nhanh và boilerplate tối thiểu. Trong năm 2026, thư viện này đối mặt với cuộc khủng hoảng bảo trì: cập nhật không đều, phụ thuộc vào một maintainer duy nhất, và sự không tương thích ngày càng tăng với các phiên bản Flutter SDK gần đây. Bản phát hành ổn định cuối cùng (4.7.3) giải quyết tương thích Flutter 3.38 cách đây tám tháng. Các ứng dụng production sử dụng GetX gặp phải vấn đề lifecycle controller và rò rỉ bộ nhớ do singleton toàn cục ngầm định.
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}'));
}
}Lệnh gọi Get.put() đăng ký controller dưới dạng singleton toàn cục. Trong các luồng điều hướng phức tạp, controller tồn tại vượt quá phạm vi dự định, tiêu tốn bộ nhớ. Các biến reactive .obs bỏ qua hệ thống thông báo state tiêu chuẩn của Flutter, khiến việc tích hợp với các package khác trở nên không đáng tin cậy.
Migration từ GetX sang Riverpod: Từng bước một
Đối với các nhóm đang bảo trì codebase GetX, quá trình migration sang Riverpod có thể tiến hành từng bước. Cả hai thư viện cùng tồn tại trong một dự án, cho phép chuyển đổi từng màn hình mà không cần viết lại toàn bộ.
// 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.4)
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.4)
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]),
),
);
}
}Phiên bản Riverpod xử lý các trạng thái loading, error và data một cách tường minh thông qua AsyncValue.when(). Không có singleton toàn cục, không cần quản lý lifecycle thủ công, và tự động dispose khi widget unmount.
So sánh hiệu năng: Hiệu quả Rebuild
Hiệu quả rebuild ảnh hưởng trực tiếp đến frame rate. Mỗi giải pháp xử lý rebuild widget theo cách khác nhau, và sự khác biệt trở nên đo lường được trong các danh sách có hàng trăm item.
| Chỉ số | Riverpod 3.4 | Bloc 9.1 | GetX |
|---|---|---|---|
| Rebuild có chọn lọc | select() filter | BlocSelector | .obs per field |
| An toàn compile-time | Đầy đủ (code gen) | Một phần (sealed classes) | Không có |
| Auto-dispose | Tích hợp sẵn | Thủ công qua close() | Không đáng tin cậy |
| Tạm dừng khi off-screen | Tự động | Thủ công | Không hỗ trợ |
| Truy vết event | Provider observer | Log event đầy đủ | Không có |
| Cô lập kiểm thử | ProviderContainer.test() | blocTest helper | Cần Get.testMode |
| Tác động kích thước bundle | ~45KB | ~38KB | ~120KB (bao gồm routing, DI, HTTP) |
Phương thức select() của Riverpod và BlocSelector của Bloc đều cho phép rebuild chính xác, chỉ cập nhật subtree widget phụ thuộc vào dữ liệu thay đổi. .obs của GetX đạt được mức độ chi tiết tương tự trên mỗi field nhưng thiếu xác minh compile-time đối với đồ thị dependency.
GetX gộp chung routing, dependency injection, HTTP client và state management trong một package duy nhất. Ứng dụng chỉ sử dụng state management vẫn phải import toàn bộ thư viện 120KB. Riverpod và Bloc là các package tập trung, mỗi cái làm tốt một nhiệm vụ duy nhất.
Chiến lược kiểm thử cho từng giải pháp
Khả năng kiểm thử thường quyết định giải pháp nào có thể mở rộng cùng với sự phát triển của đội ngũ. Mỗi thư viện có cách tiếp cận kiểm thử khác nhau.
// 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() của Riverpod tạo đồ thị dependency cô lập cho mỗi test. Helper blocTest của Bloc xác minh chuỗi chuyển đổi state chính xác, phù hợp với kiến trúc event-driven. Kiểm thử GetX yêu cầu thiết lập Get.testMode = true và quản lý lifecycle controller thủ công, thường dẫn đến các test không ổn định trong môi trường CI.
Quản lý state trong Flutter nằm trong số các chủ đề được hỏi nhiều nhất trong phỏng vấn lập trình viên mobile. Việc hiểu rõ sự đánh đổi giữa Riverpod, Bloc và GetX thể hiện sự trưởng thành về tư duy kiến trúc. Hãy thực hành giải thích khi nào mỗi giải pháp phù hợp và khi nào không phù hợp.
Ma trận quyết định: Chọn giải pháp phù hợp
Các ràng buộc của dự án quyết định lựa chọn tốt nhất. Quy mô đội ngũ, yêu cầu pháp lý và codebase hiện tại đều là các yếu tố trong quá trình ra quyết định.
Riverpod 3.4 phù hợp khi đội ngũ coi trọng tính an toàn compile-time, dự án cần fetching dữ liệu bất đồng bộ với khả năng phục hồi lỗi tự động, hoặc codebase được xây dựng từ đầu. Tích hợp ValueListenable trong phiên bản 3.4 giúp dễ dàng áp dụng hơn trong các dự án có sẵn code animation. Đường cong học tập ở mức vừa phải: những lập trình viên đã quen với Provider có thể chuyển đổi một cách tự nhiên.
Bloc 9.1 phù hợp khi dự án hoạt động trong ngành công nghiệp được quản lý chặt chẽ (fintech, y tế), đội ngũ cần truy vết event đầy đủ cho mục đích kiểm toán, hoặc ứng dụng xử lý các quy trình concurrent phức tạp như xử lý thanh toán. Chi phí boilerplate được đền đáp bằng khả năng bảo trì ở quy mô lớn.
GetX chỉ phù hợp khi bảo trì codebase GetX hiện có mà chi phí migration vượt quá ngân sách khả dụng. Bắt đầu dự án mới với GetX trong năm 2026 đồng nghĩa với việc tạo ra nợ kỹ thuật ngay từ ngày đầu tiên. Tài liệu chính thức của Flutter không liệt kê GetX trong số các giải pháp được khuyến nghị.
Để thực hành sâu hơn về các mẫu quản lý state trong Flutter, module cơ bản về quản lý state Flutter bao gồm các khái niệm nền tảng được kiểm tra trong phỏng vấn. Module mẫu provider khám phá các chiến lược dependency injection áp dụng cho cả ba giải pháp. Về chiến lược kiểm thử dành riêng cho widget Flutter, xem Flutter Testing: Widget Tests and Integration Tests.
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Nguồn tham khảo
- Riverpod Changelog - lịch sử phiên bản và breaking changes từ 3.0 đến 3.4.2
- flutter_bloc Changelog - thay đổi Bloc 9.0 đến 9.1 và mounted safety checks
- GetX Changelog - bản phát hành cuối 4.7.3 giải quyết tương thích Flutter 3.38
- Flutter State Management Options - khuyến nghị chính thức của đội ngũ Flutter
So sánh State Management: Điểm chính dành cho Developer Flutter
- Riverpod 3.4 cung cấp tính an toàn compile-time thông qua code generation, retry tự động cho các provider bị lỗi, hỗ trợ pause/resume giúp giảm tiêu thụ pin, và tích hợp
ValueListenablemới cho workflow animation - Bloc 9.1 áp dụng chuyển đổi state dựa trên event với khả năng kiểm toán đầy đủ, trở thành tiêu chuẩn cho ứng dụng doanh nghiệp trong các ngành công nghiệp được quản lý
- GetX đối mặt với cuộc khủng hoảng bảo trì năm 2026 với các bản cập nhật không đều và sự không tương thích SDK ngày càng tăng; các dự án GetX hiện có nên lên kế hoạch migration dần sang Riverpod
- Migration từ GetX sang Riverpod được thực hiện từng màn hình mà không cần viết lại toàn bộ ứng dụng, vì cả hai thư viện cùng tồn tại trong cùng một dự án
- Cô lập kiểm thử khác biệt đáng kể: Riverpod sử dụng
ProviderContainer.test(), Bloc sử dụngblocTestvới xác minh chuỗi event, và GetX yêu cầu cấu hình test mode toàn cục dễ hỏng - Kích thước bundle quan trọng trên mobile: Riverpod (~45KB) và Bloc (~38KB) cung cấp các package tập trung, trong khi GetX (~120KB) mang theo các tính năng không sử dụng
Bắt đầu luyện tập!
Kiểm tra kiến thức với mô phỏng phỏng vấn và bài kiểm tra kỹ thuật.
Bạn có tìm ra lỗi trong Flutter không?
Một đoạn mã thật, một lỗi ẩn, mỗi ngày một lượt. Không cần tài khoản để thử.

Viết bởi
Anthony Fillion-MailletNgười sáng lập SharpSkill
Lập trình viên fullstack hơn 10 năm. Anh điều hành SharpSkill và chịu trách nhiệm về mọi nội dung đăng tại đây.
Cập nhật ngày 19 tháng 8, 2026
Thẻ
Chia sẻ
Bài viết liên quan

Quản lý State trong Flutter: Riverpod vs BLoC - Hướng dẫn So sánh Đầy đủ
So sánh chi tiết giữa Riverpod 3.x và BLoC 9.x cho quản lý state trong Flutter. Kiến trúc, automatic retry, khả năng kiểm thử và các trường hợp sử dụng để chọn giải pháp tốt nhất.

Dart Isolates và Concurrency 2026: compute, Async và Câu Hỏi Phỏng Vấn
Hướng dẫn toàn diện về Dart isolates, hàm compute trong Flutter, và các pattern concurrency để xây dựng ứng dụng responsive năm 2026.

Top 20 Câu Hỏi Phỏng Vấn Flutter Dành Cho Lập Trình Viên Mobile
Chuẩn bị cho buổi phỏng vấn Flutter với 20 câu hỏi thường gặp nhất. Widget, quản lý state, Dart, kiến trúc và các best practice được giải thích chi tiết kèm code mẫu.