Quản lý Trạng thái với Bloc

1. Bloc là gì ?
– Bloc là một trong những kiến trúc dùng để quản lý trạng thái và xử lý logic trong flutter.
– Bloc sẽ tách phần giao diện (UI) và logic code thành 2 phần giúp việc quản lý code tối ưu code tốt hơn.
– Bloc sẽ chi

2. Bloc có đặc điểm gì ?

Đặc điểm chính của Bloc là:

1. Stream: Bloc sử dụng Stream để theo dõi và thông báo về các thay đổi trong trạng thái ứng dụng. Khi trạng thái thay đổi, nó sẽ phát ra các sự kiện thông qua Stream để cập nhật giao diện người dùng.
2. Sink: Sink là một đối tượng StreamController được sử dụng để gửi các sự kiện vào Bloc. Khi người dùng tương tác với giao diện người dùng, các sự kiện được gửi vào Bloc thông qua Sink để kích hoạt xử lý logic tương ứng (sau khi xử lý sẽ state sẽ được stream mang đến giao diện và đập cái state cũ bỏ state mới cho UI).
3. State: State là trạng thái hiện tại của ứng dụng. Khi Bloc nhận được các sự kiện từ Sink, nó sẽ xử lý logic và cập nhật trạng thái mới. Các thành phần UI được đăng ký để lắng nghe trạng thái mới thông qua Stream.
4. Event: Event là các hành động hoặc sự kiện nhất định mà người dùng thực hiện trong ứng dụng, chẳng hạn như nhấn nút, kéo thả, nhập liệu, vv. Mỗi sự kiện sẽ được gửi vào Bloc thông qua Sink để kích hoạt xử lý logic tương ứng.
5. BlocProvider: BlocProvider là một widget Flutter giúp cung cấp Bloc cho các thành phần con trong cây UI. Widget này đảm bảo rằng Bloc có thể được truy cập từ bất kỳ thành phần con nào mà cần nó.

Sử dụng Bloc giúp bạn quản lý trạng thái ứng dụng một cách hiệu quả, tách biệt logic kinh doanh khỏi giao diện người dùng và tạo ra mã Flutter dễ bảo trì và tái sử dụng.

3. Ví dụ đơn giản về việc sử dụng bloc với thư viện flutter_bloc
Ta sẽ vào ví dụ với ứng dụng đơn giản (Đếm số):
– Thêm thư viện vào pubspec.yaml

flutter_bloc:

– Tạo 1 bloc counter với event, state, và bloc

-Với couter_event.dart chúng ta sẽ khai báo như sau:

part of 'couter_bloc.dart';

abstract class CouterEvent {}

final class CouterIncrementPressed extends CouterEvent {}

ở file trên tôi chỉ khai báo 1 event tăng số đếm khi thực hiện nhấn nút.

-Với couter_state.dart ta sẽ khai báo 1 biến abc để lưu trữ số hiện tại đó


part of 'couter_bloc.dart';

abstract class CouterState {
int getCouter();
}

class CouterInitial extends CouterState {
CouterInitial(this.abc);
int abc;

@override
int getCouter() {
return abc;
}
}

đến couter_bloc.dart thì ta sẽ bắt đầu xử lý logic từ event và xử lý các state tương ứng:

import 'package:flutter_bloc/flutter_bloc.dart';

part 'couter_event.dart';
part 'couter_state.dart';


class CouterBloc extends Bloc<CouterEvent, CouterInitial> {
CouterBloc() : super(CouterInitial(0)) {
on<CouterIncrementPressed>((event, emit) {
emit(CouterInitial(state.getCouter() + 1));
});
}
}

Cuối cùng hiển thị ra giao diện (main.dart) :

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:test_state_management/bloc/couter_bloc.dart';

void main() {
runApp(const MyApp());
}

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: [
BlocProvider(create: (context) => CouterBloc()),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
useMaterial3: true,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});

final String title;

@override
State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
// final controller = Get.put(CouterController());

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
BlocBuilder<CouterBloc, CouterState>(
builder: (context, state) {
if (state is CouterInitial) {
return Text('${state.abc}');
}
return const SizedBox();
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () =>
context.read<CouterBloc>().add(CouterIncrementPressed()),
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

khi nhấn nút thì giao diện sẽ được cập nhật lên

4. Ưu điểm, nhược điểm của bloc:

Ưu điểm:

1. Quản lý trạng thái: Bloc giúp quản lý trạng thái của ứng dụng một cách hiệu quả. Bằng cách sử dụng các event và state, bạn có thể kiểm soát dễ dàng việc cập nhật và hiển thị trạng thái của ứng dụng.
2. Tách biệt logic: Với bloc, bạn có thể tách biệt hoàn toàn logic kinh doanh (business logic) ra khỏi giao diện người dùng (UI). Điều này giúp mã nguồn trở nên sáng sủa hơn, dễ bảo trì và tái sử dụng.
3. Kiểm thử dễ dàng: Đối với việc kiểm thử, bloc mang lại lợi ích lớn. Bạn có thể kiểm tra logic kinh doanh mà không phải tương tác với giao diện người dùng. Điều này giúp viết các bài kiểm tra tự động (automated tests) dễ dàng hơn và đảm bảo tính ổn định của ứng dụng.
4. Mã tái sử dụng: Bloc cho phép tái sử dụng logic kinh doanh trong nhiều thành phần khác nhau của ứng dụng. Bạn có thể sử dụng cùng một bloc trong nhiều màn hình hoặc thậm chí trong các ứng dụng Flutter khác.
5. Quản lý luồng dữ liệu: Với bloc, bạn có thể quản lý luồng dữ liệu một cách rõ ràng. Bloc giúp xác định rõ ràng các sự kiện (events) và trạng thái (states) để điều khiển quá trình xử lý dữ liệu trong ứng dụng.
6. Cộng đồng mạnh mẽ: Flutter Bloc là một thư viện phổ biến và có một cộng đồng lớn hỗ trợ. Bạn có thể tìm thấy nhiều tài liệu, ví dụ mã nguồn và hướng dẫn từ cộng đồng này để giúp bạn triển khai Bloc trong ứng dụng Flutter của mình.

Nhược điểm:

1. Khó khăn trong việc học và hiểu: Bloc là một kiến trúc phức tạp và yêu cầu một quá trình học tập đáng kể để hiểu rõ cách hoạt động. Đối với những người mới bắt đầu với Flutter, việc sử dụng bloc có thể gây khó khăn và tốn thời gian để làm quen.
2. Cần viết nhiều mã: Sử dụng bloc yêu cầu bạn phải viết nhiều mã để xử lý logic và trạng thái ứng dụng. Điều này có thể làm cho mã của bạn trở nên phức tạp và dễ bị lỗi nếu không được quản lý cẩn thận.
3. Cấu hình phức tạp: Để sử dụng bloc, bạn cần thiết lập các lớp, khai báo sự kiện, trạng thái và các luồng dữ liệu tương ứng. Việc cấu hình phức tạp này có thể gây khó khăn và tăng đáng kể thời gian phát triển ứng dụng.
4. Quản lý trạng thái toàn cục: Khi sử dụng bloc, trạng thái của ứng dụng được lưu trữ trong các lớp Bloc và được chia sẻ toàn cục. Điều này có thể gây ra vấn đề khi bạn cần quản lý nhiều trạng thái khác nhau hoặc khi phải xử lý các truy cập và cập nhật đồng thời từ nhiều thành phần khác nhau.
5. Đòi hỏi kiến thức bổ sung: Sử dụng bloc yêu cầu bạn hiểu rõ các khái niệm liên quan đến Reactive Programming, Streams và Providers trong Flutter. Nếu bạn không quen thuộc với những khái niệm này, việc sử dụng bloc có thể trở nên phức tạp và khó hiểu.

5. Kết luận:

Mô hình Bloc là một mô hình quản lý trạng thái giúp quản lý trạng thái ứng dụng và làm cho việc xây dựng ứng dụng phức tạp dễ dàng hơn. Nó mang lại một số lợi ích như phân tách các vấn đề, khả năng kiểm thử, tính sử dụng mã, hiệu suất cải thiện và khả năng mở rộng.

Chúng ta cũng đã thảo luận về nhược điểm của việc sử dụng mô hình Bloc, chẳng hạn như sự phức tạp, độ khó cao, chi phí thêm và bảo trì. Tuy nhiên, những nhược điểm này có thể được giảm bớt bằng cách tuân theo các quy tắc để xây dựng ứng dụng với mô hình Bloc.

Cuối cùng, chúng ta đã thảo luận về các gói công cụ để triển khai mô hình Bloc trong Flutter, bao gồm bloc, flutter_bloc

Tổng quan, mô hình Bloc là một mô hình quản lý trạng thái mạnh mẽ có thể giúp ứng dụng Flutter của bạn dễ bảo trì và có khả năng mở rộng. Bằng cách tuân theo các nguyên tắc tốt nhất và sử dụng các gói công cụ phù hợp, bạn có thể xây dựng ứng dụng Flutter hiệu quả và mạnh mẽ với mô hình Bloc.