Hi我是鱼板伯爵,本次教学会用一个简单的加一减一的范例来教大家 Bloc 这个套件,当你学会以後程序码会变得非常乾净,但是内容可能会有一点难希望大家可以看得懂。
Bloc 可以让页面与逻辑分离变得容易管理,你可以想像他是页面与逻辑的桥梁,让程序码可以快速阅读、易於测试和可重复使用,这就是为什麽我要介绍他的原因,以下是他的套件,如果你是用vscode的同学建议你可以安装bloc,这个套件可以为你生成基本架构,equatable
则是可以简化程序码。
dependencies:
flutter:
sdk: flutter
bloc: ^7.0.0
flutter_bloc: ^7.0.1
equatable: ^2.0.3
为让大家更容易理解,可以先把这个套件当成一台贩卖机,你对贩卖机投了10元(Event),卖贩机检查你的金额(Bloc),确认金额10元後萤幕显示10元(State),10元的饮料按钮就会亮灯。
如果有安装 vscode 的 bloc 套件,只需要点击右键就可以创建一个 Bloc 的基本架构,分别为 Event、State 和Bloc。
我们的功能主要就只有两个,第一个是加一,第二个是减一。先创建一个结构来定义我们的函式,我们可以在他上面写一些说明文件,如果滑鼠移到上面则会说明这个API的用途。
abstract class CountRepositoryImp {
/// Count model
///
/// Increment one
Future<int> add(int count);
/// Count model
///
/// Decrement one
Future<int> dec(int count);
}
我们可以在一个Class後面加上implements刚刚创的API,接着对Class的按下快捷键(mac:command
+.
,win:control
+.
),就会跳出一个选项来生成我们的函式框架,然後就把我们的功能给实作出来。
class CountRepository implements CountRepositoryImp {
@override
Future<int> add(count) async {
return ++count;
}
@override
Future<int> dec(int count) async {
return --count;
}
}
使用者处发的事件有两个加一和减一,先在这两个Event里面宣告count
,让我们的数字作加减,当count
传进触发的是加一事件,那他就必须把我的count
加一後传回来给我,toString则是可以让我们看到事件触发时传进去的内容。
part of 'mybloc_bloc.dart';
abstract class MyblocEvent extends Equatable {
const MyblocEvent();
@override
List<Object> get props => [];
}
class IncrementEvent extends MyblocEvent {
final int count;
const IncrementEvent(this.count);
@override
String toString() => 'IncrementEvent(count: $count)';
}
class DecrementEvent extends MyblocEvent {
final int count;
const DecrementEvent(this.count);
@override
String toString() => 'DecrementEvent(count: $count)';
}
我的状态分别切成MyblocInitial(初始)、Success(成功)和Failure(失败)三个状态,接着在Success里宣告一个count来将处理完的count传进来,然後对状态名称案下快捷键,选择equatable就会生成@override
的三段程序码,不过我们有安装equatable这个套件,因此我们可以把他缩短成一段让我们程序码更有效率,而这几段程序码则是帮我们检查物件是不是相同,不是的话就可以帮我们做覆盖的动作。
未使用:
class Success extends MyblocState {
final int count;
const Success(this.count);
@override
String toString() => 'Increment success(count: $count)';
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is AddSuccess && other.count == count;
}
@override
int get hashCode => count.hashCode;
}
使用後:
class Success extends MyblocState {
final int count;
const Success(this.count);
@override
List<Object> get props => [count];
}
Bloc中可以分成三个,第一个是初始化,第二个是判断使用者事件,第三个是事件触发後回传的状态,在这里我们会用到Stream
,Stream
是用来接收一连串的事件,Stream
会监听目前状态,若 Stream 有事件,则将告诉监听器,而其他流程的写法就以注解的方式来介绍。
import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:day13/count_repository.dart';
import 'package:equatable/equatable.dart';
part 'mybloc_event.dart';
part 'mybloc_state.dart';
class MyblocBloc extends Bloc<MyblocEvent, MyblocState> {
CountRepository _countRepository;
// 初始化
MyblocBloc({required CountRepository countRepository})
: _countRepository = countRepository,
super(MyblocInitial());
// 触发事件
@override
Stream<MyblocState> mapEventToState(
MyblocEvent event,
) async* {
// 当使用者处发加一的事件时
if (event is IncrementEvent) {
// 调用加一的状态
yield* _mapIncrementToState(event.count);
}
// 当使用者处发减一的事件时
if (event is DecrementEvent) {
// 调用减一的状态
yield* _mapDecrementToState(event.count);
}
}
// 状态
Stream<MyblocState> _mapIncrementToState(int count) async* {
// 成功加一的话回传Success否则Failure
try {
final _count = await _countRepository.add(count);
yield Success(_count);
} catch (_) {
yield Failure();
}
}
// 状态
Stream<MyblocState> _mapDecrementToState(int count) async* {
// 成功减一的话回传Success否则Failure
try {
final _count = await _countRepository.dec(count);
yield Success(_count);
} catch (_) {
yield Failure();
}
}
}
终於到了最後如何使用,原本想说拆成上下集,但是鱼板国王不同意所以只好继续说下去,不知道大家还有没有跟上,以下我会介绍Bloc的其中一种用法。
BlocProvider
负责创建Bloc和一个元件,你将可以使用Bloc所有事件。
void main() {
final countRepository = CountRepository();
runApp(
BlocProvider<MyblocBloc>(
create: (context) => MyblocBloc(countRepository: countRepository),
child: MyApp(),
),
);
}
BlocBuilder
可以处理构建小部件以回应新状态。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: BlocBuilder<MyblocBloc, MyblocState>(
builder: (context, state) {
log("$state");
if (state is Success) {
return MyHomePage(count: state.count);
} else {
// state is Failure
return MyHomePage(count: 0);
}
},
),
);
}
}
我们只需要在按钮onPressed
中写下触发的事件就可以了。
TextButton(
onPressed: () {
BlocProvider.of<MyblocBloc>(context)
.add(IncrementEvent(widget._count));
},
child: Text("Add"),
),
Bloc还有其他的功能,MultiBlocProvider(创建多个Bloc)、BlocListener(听取状态但不能改变元件)、MultiBlocListener(听取多个状态但不能改变元件)等等...,就让大家自己摸索一下罗。
<<: [13th-铁人赛]Day 7:Modern CSS 超详细新手攻略 - 伪类 Pseudo Classes
>>: 33岁转职者的前端笔记-DAY 13 图片格式及影音格式
Hi Dai Gei Ho~ 我是Winnie~ 今天终於来到我的第七天,按照七天养成一个好习惯的说...
step 1.import script.js 2.add export_content <d...
Date / Time pickers 可以透过官方文件知道有两种做法,一种是利用原生 input ...
Photo on gatling.io 前言 前几周小弟介绍了一款负载性能的...
问题: $ man gcc No manual entry for gcc 看到这个方法,但是失败了...