本节目标

  • 为什么要用 bloc
  • bloc vs provider
  • 学习路线推荐
  • 安装 bloc vscode 插件
  • 配置 bloc 依赖包
  • 编写计算器示例

视频

https://www.bilibili.com/video/BV1ef4y1e79o/

代码

https://github.com/ducafecat/flutter-bloc-learn/tree/master/ducafecat_bloc_start_example

正文

为什么要用 bloc

  • 状态管理(这是必须的)

  • 三层分离

    • 表现层(Presentation)
    • 业务逻辑(Business Logic)
    • 数据层(Data)
      • 数据源/库(Repository)
      • 数据提供者(Data Provider)
  • 规范组内开发

  • 方便的 测试、记录 用户行为

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class SimpleBlocDelegate extends BlocDelegate {
@override
void onEvent(Bloc bloc, Object event) {
super.onEvent(bloc, event);
print('${bloc.runtimeType} $event');

// 所有的UI事件
// 可用 umeng 这样平台 进行跟踪
}

@override
void onError(Bloc bloc, Object error, StackTrace stacktrace) {
super.onError(bloc, error, stacktrace);
print('${bloc.runtimeType} $error');

// 所有发生的错误
// 用 sentry 记录错误
}

@override
void onTransition(Bloc bloc, Transition transition) {
super.onTransition(bloc, transition);
print(transition);

// 所有的 State 变化
}
}

bloc vs provider

  • bloc 是一种 mvvm 基于 事件、状态 驱动的

  • provider 是基于方法的

bloc 学习路线

  • Flutter Bloc 快速上手 -> Stream -> Cubit -> Bloc

  • 理解基于 mvvm 组件化拆分

安装 bloc vscode 插件

bloc

创建项目

  • pubspec.yaml
1
2
3
4
5
6
7
8
name: ducafecat_bloc_start_example
...
dependencies:
...

bloc: ^6.1.0
flutter_bloc: ^6.1.0
equatable: ^1.2.5
  • 目录结构

counter 计算器业务下创建 bloc view 目录,这样就分离了

编写 bloc

  • lib/counter/bloc/counter_bloc.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import 'dart:async';

import 'package:bloc/bloc.dart';
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';

part 'counter_event.dart';
part 'counter_state.dart';

class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterInitial(0));

int counterNum = 0;

@override
Stream<CounterState> mapEventToState(
CounterEvent event,
) async* {
if (event is CounterIncrement) {
yield* _mapIncrementEventToState(event);
} else if (event is CounterSubduction) {
yield* _mapSubductionEventToState(event);
}
}

Stream<CounterState> _mapIncrementEventToState(
CounterIncrement event) async* {
this.counterNum += 1;
yield CounterChange(this.counterNum);
}

Stream<CounterState> _mapSubductionEventToState(
CounterSubduction event) async* {
this.counterNum -= 1;
yield CounterChange(this.counterNum);
}
}
  • lib/counter/bloc/counter_event.dart
1
2
3
4
5
6
7
8
9
10
11
part of 'counter_bloc.dart';

@immutable
abstract class CounterEvent extends Equatable {
@override
List<Object> get props => [];
}

class CounterIncrement extends CounterEvent {}

class CounterSubduction extends CounterEvent {}
  • lib/counter/bloc/counter_state.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
part of 'counter_bloc.dart';

@immutable
abstract class CounterState extends Equatable {
final int value;

const CounterState(this.value);

@override
List<Object> get props => [value];
}

class CounterInitial extends CounterState {
CounterInitial(int value) : super(value);
}

class CounterChange extends CounterState {
CounterChange(int value) : super(value);
}

编写 view

  • lib/counter/view/page.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import 'package:ducafecat_bloc_start_example/counter/bloc/counter_bloc.dart';
import 'package:ducafecat_bloc_start_example/counter/view/view.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterPage extends StatelessWidget {
const CounterPage({Key key}) : super(key: key);

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => CounterBloc(),
child: CounterView(),
);
}
}
  • lib/counter/view/view.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import 'package:ducafecat_bloc_start_example/counter/bloc/counter_bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class CounterView extends StatelessWidget {
const CounterView({Key key}) : super(key: key);

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
children: [
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text('${state.value}');
},
),
RaisedButton(
child: Text('加法'),
onPressed: () {
BlocProvider.of<CounterBloc>(context).add(CounterIncrement());
},
),
RaisedButton(
child: Text('加法'),
onPressed: () {
BlocProvider.of<CounterBloc>(context).add(CounterSubduction());
},
)
],
),
),
);
}
}

替换主程序 widget

  • lib/main.dart
1
2
3
4
5
6
7
8
9
10
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
...
home: CounterPage(), //MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}

参考


© 猫哥

https://ducafecat.tech

https://ducafecat.gitee.io