Flutter bloc là gì
Phân Mục Lục Chính
Học Bloc Pattern theo cách dễ hiểu nhất
- Báo cáo
Lời mở đầu
Sau khi mình đã san sẻ xong series về Stream, RxDart thì mình liên tục san sẻ về Bloc Pattern. Mình san sẻ theo lộ trình như vậy là vì để hiểu được Bloc Pattern thì bạn nên hiểu về Stream trước. Bạn hoàn toàn có thể tìm hiểu thêm series về Stream mình viết khá không thiếu :Nội dung chính
- Học Bloc Pattern theo cách dễ hiểu nhất
- Lời mở đầu
- 1. Bloc Pattern là gì
- 2. Code minh họa
- Bước 1: Định nghĩa các Event
- Bước 2: Định nghĩa các State
- Bước 3: Code business logic trong Bloc
- Bước 4: apply bloc vào UI
- Code chạy ra sao
- 3. Build app flutter đơn giản từ code minh họa
- Kết luận
Chinh phục Stream và RxDart trong 3 nốt nhạc
Ok, giờ chúng ta bắt đầu thôi.
Bạn đang đọc: Flutter bloc là gì
1. Bloc Pattern là gì
Tất nhiên nó là một Pattern rồi, mục tiêu của Pattern này là 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 spec mới có nhu yếu sửa code business logic hay sửa UI sẽ thuận tiện sửa hơn. Code business logic được tách ra đó người ta đặt tên là Bloc ( Business Logic Component ). Bên cạnh đó, nó còn giúp tất cả chúng ta quản trị state của 1 màn hình hiển thị tốt hơn vì những state sẽ được quản ở Bloc tách biệt với UI. Chính thế cho nên, mỗi màn hình hiển thị trong app flutter tất cả chúng ta nên tạo ra 1 bloc để giải quyết và xử lý logic của màn hình hiển thị đó và quản trị state của cả màn hình hiển thị đó .Lấy ví dụ trong dự án Bất Động Sản thực tiễn lun nha, khi user click vào button tải về tức là user gửi cái link URL của ảnh vào Bloc, Bloc sẽ nhận link URL đó và giải quyết và xử lý tải về thành cái ảnh và truyền cho UI để UI hiển thị lên màn hình hiển thị .
Input sẽ được thêm vào sink
của StreamController
và phía UI sẽ sử dụng stream
để lắng nghe nhận state mỗi khi có event được add vào sink
. (Nếu bạn chưa hiểu về cách hoạt động của StreamController, bạn có thể tìm đọc tại đây)
2. Code minh họa
Để minh họa cho bloc pattern, mình sẽ sử dụng code Dart chay trước để lượt bỏ bớt code UI rườm rà rồi tiếp đến mình sẽ thiết kế xây dựng app Flutter từ code minh họa này .Bài toán ở đây là giả lập cái remote tinh chỉnh và điều khiển TV với 3 button đơn thuần là : tăng âm lượng, giảm âm lượng và mute ( tắt tiếng )Để tiến hành bloc thường thì qua 4 bước được chia ra 4 file :
Bước 1: Định nghĩa các Event
Tạo 1 file là remote_event.dart
và định nghĩa các event sau: event tăng âm lượng, giảm âm lượng và mute.
abstract class RemoteEvent {}
// event tăng âm lượng, user muốn tăng lên bao nhiêu thì truyền vào biến increment
class IncrementEvent extends RemoteEvent {
IncrementEvent(this.increment);
final int increment;
}
// event giảm âm lượng, user muốn giảm bao nhiêu thì truyền vào biến decrement
class DecrementEvent extends RemoteEvent {
DecrementEvent(this.decrement);
final int decrement;
}
// event mute
class MuteEvent extends RemoteEvent {}
Bước 2: Định nghĩa các State
Tạo 1 file là remote_state.dart
, ở đây app mình chỉ quan tâm đến data duy nhất là (volume
) âm lượng để update UI.
class RemoteState {
RemoteState(this.volume);
final int volume;
}
Bước 3: Code business logic trong Bloc
Tạo 1 file là remote_bloc.dart
để code business logic. Như mình đã giới thiệu ở trên, nhiệm vụ của bloc là nhận event từ UI, phân biệt UI vừa gửi cho mình event gì (có thể là event tăng âm lượng hoặc mute, …) để xử lý chúng và truyền state lại cho UI.
import 'dart:async';
import 'remote_event.dart';
import 'remote_state.dart';
class RemoteBloc {
var state = RemoteState(70); // init giá trị khởi tạo của RemoteState. Giả sử TV ban đầu có âm lượng 70
// tạo 2 controller
// 1 cái quản lý event, đảm nhận nhiệm vụ nhận event từ UI
final eventController = StreamController();
// 1 cái quản lý state, đảm nhận nhiệm vụ truyền state đến UI
final stateController = StreamController();
RemoteBloc() {
// lắng nghe khi eventController push event mới
eventController.stream.listen((RemoteEvent event) {
// người ta thường tách hàm này ra 1 hàm riêng và đặt tên là: mapEventToState
// đúng như cái tên, hàm này nhận event xử lý và cho ra output là state
if (event is IncrementEvent) {
// nếu eventController vừa add vào 1 IncrementEvent thì chúng ta xử lý tăng âm lượng
state = RemoteState(state.volume + event.increment);
} else if (event is DecrementEvent) {
// xử lý giảm âm lượng
state = RemoteState(state.volume - event.decrement);
} else {
// xử lý mute
state = RemoteState(0);
}
// add state mới vào stateController để bên UI nhận được
stateController.sink.add(state);
});
}
// khi không cần thiết thì close tất cả controller
void dispose() {
stateController.close();
eventController.close();
}
}
Bước 4: apply bloc vào UI
Cuối cùng sẽ code vào file có UI, giả sử là file main.dart
import 'remote_bloc.dart';
import 'remote_event.dart';
import 'remote_state.dart';
void main() async {
// tạo đối tượng bloc
final bloc = RemoteBloc();
// UI lắng nghe state thay đổi để update UI
bloc.stateController.stream.listen((RemoteState state) {
print('Âm lượng hiện tại: ${state.volume}');
});
// giả sử 1s sau, user click vào tăng âm lượng thêm 5
await Future.delayed(Duration(seconds: 1));
bloc.eventController.sink.add(IncrementEvent(5)); // từ UI push event đến bloc
// giả sử 2s sau, user click vào giảm âm lượng đi 10
await Future.delayed(Duration(seconds: 2));
bloc.eventController.sink.add(DecrementEvent(10)); // từ UI push event đến bloc
// giả sử 3s sau, user click vào mute luôn
await Future.delayed(Duration(seconds: 3));
bloc.eventController.sink.add(MuteEvent()); // từ UI push event đến bloc
}
Run chương trình và tất cả chúng ta nhận được output là :
Âm lượng hiện tại: 75 // từ 70 tăng thêm 5
Âm lượng hiện tại: 65 // từ 75 giảm xuống 10
Âm lượng hiện tại: 0 // mute
Code chạy ra sao
Ở đây ta có 2 StreamController là eventController
và stateController
. Khi user thực hiện thao tác gì đó (ví dụ click vào mute) thì chúng ta sẽ push event input đó vào eventController.sink
và đăng ký lắng nghe event đó tại eventController.stream
(ngay khi khởi tạo đối tượng RemoteBloc là mình đã cho lắng nghe nhận event ngay). Vậy là chúng ta đã nhận được event input từ user input, chúng ta sẽ xử lý event đó (nếu là tăng âm lượng thì xử lý tăng, giảm thì xử lý giảm và mute thì xử lý mute). Xử lý xong cho ra output là state chúng ta sẽ push event state đó vào stateController.sink
và ở phía UI sẽ đăng ký nhận event từ stateController.stream
để update UI.
Event từ user input ->
add vào eventController.sink ->
eventController.stream nhận event đó ->
code logic xử lý event đó cho ra output là state ->
add state đó vào stateController.sink ->
stateController.stream nhận state đó ->
ở UI đăng ký lắng nghe stateController.stream và update UI
3. Build app flutter đơn giản từ code minh họa
Giờ chúng ta sẽ áp dụng code mô phỏng cái remote TV trên vào app thật. Có 1 widget hỗ trợ chúng ta làm update UI là StreamBuilder
. 3 file remote_event.dart
, remote_state.dart
, remote_bloc.dart
sẽ được tái sử dụng lại nhé. Chúng ta sẽ chỉ replace code trong main.dart
Vì nhìn code UI hơi rối rắm nên mình có đánh dấu code <=== new
đó là những chỗ apply code minh họa ở phần 2 vào UI nha. Chỉ cần chú ý đến nó là đủ
import 'package:bloc_remote_demo/remote_bloc.dart';
import 'package:bloc_remote_demo/remote_event.dart';
import 'package:bloc_remote_demo/remote_state.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
final bloc = RemoteBloc(); // khởi tạo bloc <=== new
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder( // sử dụng StreamBuilder để lắng nghe Stream <=== new
stream: bloc.stateController.stream, // truyền stream của stateController vào để lắng nghe <=== new
initialData: bloc.state, // giá trị khởi tạo chính là volume 70 hiện tại <=== new
builder: (BuildContext context, AsyncSnapshot snapshot) {
return Text('Âm lượng hiện tại: ${snapshot.data.volume}'); // update UI <=== new
},
),
),
floatingActionButton: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
FloatingActionButton(
onPressed: () => bloc.eventController.sink.add(IncrementEvent(5)), // add event <=== new
child: Icon(Icons.volume_up),
),
FloatingActionButton(
onPressed: () => bloc.eventController.sink.add(DecrementEvent(10)), // add event <=== new
child: Icon(Icons.volume_down),
),
FloatingActionButton(
onPressed: () => bloc.eventController.sink.add(MuteEvent()), // add event <=== new
child: Icon(Icons.volume_mute),
)
],
),
);
}
@override
void dispose() {
super.dispose();
bloc.dispose(); // dispose bloc <=== new
}
}
Đây là thành quả:
Kết luận
Như vậy, mình đã giới thiệu về gốc gác của Bloc Pattern, bây giờ để có thể tăng tốc độ code các bạn có thể học cách sử dụng các package như flutter_bloc
.
Nguồn tìm hiểu thêm : https://medium.com/flutterpub/bloc-state-management-with-easy-approach-b53fb6d15829
https://www.youtube.com/watch?v=oxeYeMHVLII&list=PLB6lc7nQ1n4jCBkrirvVGr5b8rC95VAQ5&index=1&ab_channel=ResoCoder
Source: https://suachuatulanh.edu.vn
Category: Thay Bloc Tủ Lạnh