Bloc, Provider, GetX. Đâu là thư viện quản lí state Flutter tốt nhất?
Mở đầu
Khi nói đến quản lý state trong Flutter, có một số lựa chọn phổ biến như Bloc, Provider, GetX, và các thư viện khác. Mỗi thư viện đều có những ưu và nhược điểm riêng. Vậy đâu mới là sự lựa chọn tối ưu cho dự án? Dưới đây là một phân tích về các thư viện này. Mọi người có đóng góp gì hãy để lại bình luận bên dưới nha.
Ưu, nhược điểm và demo cho từng thư viện
1. Bloc (Business Logic Component):
- Bloc là một mô hình kiến trúc được thiết kế để phân tách logic nghiệp vụ khỏi giao diện người dùng.
- Nó sử dụng các Streams và Sinks để truyền dữ liệu giữa các thành phần khác nhau.
- Bloc thường được sử dụng cùng với các thư viện hỗ trợ như RxDart hoặc flutter_bloc.
- Ưu điểm: Mô hình rõ ràng, dễ kiểm thử, dễ mở rộng, và tách biệt logic nghiệp vụ khỏi giao diện người dùng.
- Nhược điểm: Cấu trúc phức tạp hơn, và có thể gây ra lãng phí bộ nhớ nếu không quản lý đúng cách, sẽ khó học cho người mới bắt đầu hay dùng cho các dự án nhỏ.
Demo:
Ví dụ sử dụng thư viện flutter_bloc
để quản lý trạng thái đếm số.
// counter_event.dart
abstract class CounterEvent {}
class IncrementEvent extends CounterEvent {}
// counter_bloc.dart
import 'package:bloc/bloc.dart';
import 'counter_event.dart';
class CounterBloc extends Bloc<CounterEvent, int> {
CounterBloc() : super(0);
Stream<int> mapEventToState(CounterEvent event) async* {
if (event is IncrementEvent) {
yield state + 1;
}
}
}
// main.dart
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_bloc.dart';
import 'counter_event.dart';
void main() {
runApp(
BlocProvider(
create: (_) => CounterBloc(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Bloc Demo')),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, count) {
return Text(
'$count',
style: TextStyle(fontSize: 24),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<CounterBloc>().add(IncrementEvent());
},
child: Icon(Icons.add),
),
),
);
}
}
Phân tích:
- Tạo một lớp
CounterEvent
để đại diện cho sự kiện tăng biến đếm. - Tạo một
Bloc
tên làCounterBloc
kế thừa từBloc<CounterEvent, int>
để xử lý sự kiện và quản lý trạng thái đếm số. - Sử dụng
BlocProvider
để cung cấp đối tượngCounterBloc
cho toàn bộ ứng dụng. - Trong
build
củaMyApp
, sử dụngBlocBuilder
để lắng nghe sự thay đổi củaCounterBloc
và cập nhật giao diện. - Khi nhấn nút
FloatingActionButton
, gọi phương thứcadd
củaCounterBloc
với sự kiệnIncrementEvent
để tăng biến đếm và cập nhật giao diện.
2. Provider:
- Provider là một thư viện quản lý state đơn giản và nhỏ gọn.
- Nó sử dụng mô hình InheritedWidget để truyền data xuống cây widget.
- Provider cung cấp các lớp như ChangeNotifierProvider, StreamProvider, và FutureProvider để quản lý các loại state khác nhau.
- Ưu điểm: Dễ học, dễ sử dụng, và đơn giản hóa việc quản lý state.
- Nhược điểm: Có thể gây ra vấn đề hiệu suất nếu sử dụng quá nhiều provider, và không có cấu trúc rõ ràng cho logic nghiệp vụ.
Demo:
Ví dụ này sử dụng ChangeNotifierProvider
để quản lý trạng thái của một đối tượng đếm số.
// counter_provider.dart
import 'package:flutter/material.dart';
class CounterProvider extends ChangeNotifier {
int _count = 0;
int get count => _count;
void increment() {
_count++;
notifyListeners();
}
}
// main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'counter_provider.dart';
void main() {
runApp(
ChangeNotifierProvider(
create: (_) => CounterProvider(),
child: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Provider Demo')),
body: Center(
child: Consumer<CounterProvider>(
builder: (context, counterProvider, child) {
return Text(
'${counterProvider.count}',
style: TextStyle(fontSize: 24),
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
context.read<CounterProvider>().increment();
},
child: Icon(Icons.add),
),
),
);
}
}
Phân tích:
- Tạo một lớp
CounterProvider
kế thừa từChangeNotifier
để quản lý trạng thái đếm số. - Sử dụng
ChangeNotifierProvider
để cung cấp đối tượngCounterProvider
cho toàn bộ ứng dụng. - Trong
build
củaMyApp
, sử dụngConsumer
để lắng nghe sự thay đổi củaCounterProvider
và cập nhật giao diện. - Khi nhấn nút
FloatingActionButton
, gọi phương thứcincrement
củaCounterProvider
để tăng biến đếm và thông báo choConsumer
cập nhật giao diện.
3. GetX:
- GetX là một thư viện quản lý state, định tuyến, và thực hiện các tác vụ khác trong Flutter.
- Nó cung cấp các tính năng như quản lý dependencies, quản lý state, định tuyến, và quản lý thủ tục.
- GetX sử dụng reactive programming để quản lý state và cung cấp các lớp như GetBuilder, Obx, và GetX để xây dựng giao diện người dùng.
- Ưu điểm: Tích hợp nhiều tính năng, cú pháp ngắn gọn, và dễ học.
- Nhược điểm: Có thể gây ra vấn đề hiệu suất nếu sử dụng quá nhiều GetBuilder hoặc Obx, và thiếu tính linh hoạt trong một số trường hợp.
Demo:
Ví dụ sử dụng thư viện get
để quản lý trạng thái đếm số.
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class CounterController extends GetxController {
final _count = 0.obs;
int get count => _count.value;
void increment() {
_count.value++;
}
}
void main() {
runApp(
GetMaterialApp(
home: MyApp(),
),
);
}
class MyApp extends StatelessWidget {
final CounterController controller = Get.put(CounterController());
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('GetX Demo')),
body: Center(
child: Obx(
() => Text(
'${controller.count}',
style: TextStyle(fontSize: 24),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.increment,
child: Icon(Icons.add),
),
);
}
}
Phân tích:
- Tạo một lớp
CounterController
kế thừa từGetxController
để quản lý trạng thái đếm số. - Sử dụng
Get.put
để khởi tạo và cung cấp đối tượngCounterController
cho toàn bộ ứng dụng. - Trong
build
củaMyApp
, sử dụngObx
để lắng nghe sự thay đổi của biến_count
và cập nhật giao diện. - Khi nhấn nút
FloatingActionButton
, gọi phương thứcincrement
củaCounterController
để tăng biến đếm và cập nhật giao diện.
Phân tích chung cho từng thư viện quản lí state
Sau khi đánh giá các ưu và nhược điểm của các thư viện, mình thấy không có một lựa chọn tuyệt đối tốt nhất cho mọi trường hợp. Sự lựa chọn phụ thuộc vào yêu cầu của dự án, quy mô của ứng dụng, và kinh nghiệm của nhóm phát triển.
-
Provider có thể là lựa chọn tốt nhất vì nó đơn giản và dễ sử dụng. Tuy nhiên, khi dự án lớn lên, việc sử dụng quá nhiều Provider có thể gây ra vấn đề hiệu suất và khó quản lý.
-
Bloc là một lựa chọn tuyệt vời cho các ứng dụng lớn và phức tạp, nơi bạn cần tách biệt logic nghiệp vụ khỏi giao diện người dùng. Mục đích việc tách code business logic ra khỏi UI thay vì code gộp chung cả logic và UI vô cùng 1 file, để sau này tài liệu mới có yêu cầu sửa code business logic hay sửa UI sẽ dễ dàng sửa hơn. Chính vì tách biệt logic và UI nên BLoc rất được tin dùng cho các dự án lớn, cần maintain, có tính scale tốt,... BLoc khá giống mô hình MVVM. Bloc có cấu trúc phức tạp, vì vậy nó có thể không quá phù hợp với các dự án nhỏ hoặc các nhóm phát triển mới bắt đầu với Flutter.
-
GetX là một lựa chọn tuyệt vời nếu bạn đang tìm kiếm một thư viện đa năng, cung cấp nhiều tính năng khác nhau như quản lý state, định tuyến, và quản lý dependencies. Tuy nhiên, GetX có thể gây ra vấn đề hiệu suất nếu sử dụng quá nhiều GetBuilder hoặc Obx, và thiếu tính linh hoạt trong một số trường hợp. GetX hay được khuyên dùng cho các dự án nhỏ và cho người mới bắt đầu tiếp cận với quản lí state trong Flutter.
Kết
Không có một lựa chọn tốt nhất cho mọi trường hợp. Bạn nên cân nhắc yêu cầu của dự án, quy mô của ứng dụng, và kinh nghiệm của nhóm phát triển để lựa chọn thư viện quản lý state phù hợp nhất. Ngoài ra, bạn cũng có thể kết hợp các thư viện khác nhau trong cùng một dự án để tận dụng những ưu điểm của chúng.
Tham khảo thêm các document của 3 thư viện trên tại:
Bloc: https://bloclibrary.dev/
Provider: https://pub.dev/packages/provider
All rights reserved