在 BLoC 中触发初始事件
Triggering initial event in BLoC
example_states:
abstract class ExampleState extends Equatable {
const ExampleState();
}
class LoadingState extends ExampleState {
//
}
class LoadedState extends ExampleState {
//
}
class FailedState extends ExampleState {
//
}
example_events:
abstract class ExampleEvent extends Equatable {
//
}
class SubscribeEvent extends ExampleEvent {
//
}
class UnsubscribeEvent extends ExampleEvent {
//
}
class FetchEvent extends ExampleEvent {
//
}
example_bloc:
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
@override
ExampleState get initialState => LoadingState();
@override
Stream<ExampleState> mapEventToState(
ExampleEvent event,
) async* {
if (event is SubscribeEvent) {
//
} else if (event is UnsubscribeEvent) {
//
} else if (event is FetchEvent) {
yield LoadingState();
try {
// network calls
yield LoadedState();
} catch (_) {
yield FailedState();
}
}
}
}
example_screen:
class ExampleScreenState extends StatelessWidget {
// ignore: close_sinks
final blocA = ExampleBloc();
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<ExampleBloc, ExampleState>(
bloc: blocA,
// ignore: missing_return
builder: (BuildContext context, state) {
if (state is LoadingState) {
blocA.add(Fetch());
return CircularProgressBar();
}
if (state is LoadedState) {
//...
}
if (state is FailedState) {
//...
}
},
),
);
}
}
如您在 example_bloc 中所见,初始状态为 LoadingState() 并且在构建中它显示循环进度条。我使用 Fetch() 事件来触发下一个状态。但是我在那里使用它感觉不舒服。我想做的是:
当应用程序启动时,它应该显示 LoadingState 并开始网络调用,然后当它全部完成时,它应该显示 LoadedState 和网络调用结果,如果出现问题则显示 FailedState 。我想不做就实现这些
if (state is LoadingState) {
blocA.add(Fetch());
return CircularProgressBar();
}
您的不适确实有原因 - build()
方法不应触发任何事件(构建()可以根据 Flutter 框架的需要触发多次)
我们的案例是在 Bloc 创建时触发初始事件
可能性概览
- 使用 BlocProvider 插入 Bloc 的情况 - 这是首选方式
create:
仅在安装 BlocProvider 时触发一次回调,并且在卸载 BlocProvider 时 BlocProvider 将关闭 () bloc
class ExampleScreenState extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => ExampleBloc()..add(Fetch()), // <-- first event,
child: BlocBuilder<ExampleBloc, ExampleState>(
builder: (BuildContext context, state) {
...
},
),
),
);
}
}
- 在 Statefull 小部件的
State
中创建 Bloc 的情况
class _ExampleScreenStateState extends State<ExampleScreenState> {
ExampleBloc _exampleBloc;
@override
void initState() {
super.initState();
_exampleBloc = ExampleBloc();
_exampleBloc.add(Fetch());
// or use cascade notation
// _exampleBloc = ExampleBloc()..add(Fetch());
}
@override
void dispose() {
super.dispose();
_exampleBloc.close(); // do not forget to close, prefer use BlocProvider - it would handle it for you
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<ExampleBloc, ExampleState>(
bloc: _exampleBloc,
builder: (BuildContext context, state) {
...
},
),
);
}
}
- 在创建 Bloc 实例时添加第一个事件 - 这种方式在测试时有缺点,因为第一个事件是隐式的
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
...
ExampleBloc() {
add(Fetch());
}
}
// insert it to widget tree with BlocProvider or create in State
BlocProvider( create: (_) => ExampleBloc(), ...
// or in State
class _ExampleScreenStateState extends State<ExampleScreenState> {
final _exampleBloc = ExampleBloc();
...
PS 欢迎在评论中联系我
Sergey Salnikov 给出了很好的答案。不过我想我可以添加另一个建议。
在我的 main.dart 文件中,我使用 MultiBlocProvider 创建我的所有块,以便在树的下方使用。像这样
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: <BlocProvider<dynamic>>[
BlocProvider<OneBloc>(create: (_) => OneBloc()),
BlocProvider<TwoBloc>(create: (_) => TwoBloc()),
],
child: MaterialApp( // Rest of your app )
然后,当我需要在加载页面时调用事件时,在这种情况下,我想根据所选的列表块获取一些数据,并且我需要比 FutureBuilder 可以提供的更多选项,我简单地使用了 initState ();并调用了 bloc 提供者并添加了一个事件。
class _ExampleScreenState extends State<ExampleScreen> {
@override
void initState() {
super.initState();
BlocProvider.of<OneBloc>(context)
.add(FetchData);
}
之所以有效,是因为已经从根小部件提供了 bloc。
example_states:
abstract class ExampleState extends Equatable {
const ExampleState();
}
class LoadingState extends ExampleState {
//
}
class LoadedState extends ExampleState {
//
}
class FailedState extends ExampleState {
//
}
example_events:
abstract class ExampleEvent extends Equatable {
//
}
class SubscribeEvent extends ExampleEvent {
//
}
class UnsubscribeEvent extends ExampleEvent {
//
}
class FetchEvent extends ExampleEvent {
//
}
example_bloc:
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
@override
ExampleState get initialState => LoadingState();
@override
Stream<ExampleState> mapEventToState(
ExampleEvent event,
) async* {
if (event is SubscribeEvent) {
//
} else if (event is UnsubscribeEvent) {
//
} else if (event is FetchEvent) {
yield LoadingState();
try {
// network calls
yield LoadedState();
} catch (_) {
yield FailedState();
}
}
}
}
example_screen:
class ExampleScreenState extends StatelessWidget {
// ignore: close_sinks
final blocA = ExampleBloc();
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<ExampleBloc, ExampleState>(
bloc: blocA,
// ignore: missing_return
builder: (BuildContext context, state) {
if (state is LoadingState) {
blocA.add(Fetch());
return CircularProgressBar();
}
if (state is LoadedState) {
//...
}
if (state is FailedState) {
//...
}
},
),
);
}
}
如您在 example_bloc 中所见,初始状态为 LoadingState() 并且在构建中它显示循环进度条。我使用 Fetch() 事件来触发下一个状态。但是我在那里使用它感觉不舒服。我想做的是:
当应用程序启动时,它应该显示 LoadingState 并开始网络调用,然后当它全部完成时,它应该显示 LoadedState 和网络调用结果,如果出现问题则显示 FailedState 。我想不做就实现这些
if (state is LoadingState) {
blocA.add(Fetch());
return CircularProgressBar();
}
您的不适确实有原因 - build()
方法不应触发任何事件(构建()可以根据 Flutter 框架的需要触发多次)
我们的案例是在 Bloc 创建时触发初始事件
可能性概览
- 使用 BlocProvider 插入 Bloc 的情况 - 这是首选方式
create:
仅在安装 BlocProvider 时触发一次回调,并且在卸载 BlocProvider 时 BlocProvider 将关闭 () bloc
class ExampleScreenState extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocProvider(
create: (context) => ExampleBloc()..add(Fetch()), // <-- first event,
child: BlocBuilder<ExampleBloc, ExampleState>(
builder: (BuildContext context, state) {
...
},
),
),
);
}
}
- 在 Statefull 小部件的
State
中创建 Bloc 的情况
class _ExampleScreenStateState extends State<ExampleScreenState> {
ExampleBloc _exampleBloc;
@override
void initState() {
super.initState();
_exampleBloc = ExampleBloc();
_exampleBloc.add(Fetch());
// or use cascade notation
// _exampleBloc = ExampleBloc()..add(Fetch());
}
@override
void dispose() {
super.dispose();
_exampleBloc.close(); // do not forget to close, prefer use BlocProvider - it would handle it for you
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: BlocBuilder<ExampleBloc, ExampleState>(
bloc: _exampleBloc,
builder: (BuildContext context, state) {
...
},
),
);
}
}
- 在创建 Bloc 实例时添加第一个事件 - 这种方式在测试时有缺点,因为第一个事件是隐式的
class ExampleBloc extends Bloc<ExampleEvent, ExampleState> {
...
ExampleBloc() {
add(Fetch());
}
}
// insert it to widget tree with BlocProvider or create in State
BlocProvider( create: (_) => ExampleBloc(), ...
// or in State
class _ExampleScreenStateState extends State<ExampleScreenState> {
final _exampleBloc = ExampleBloc();
...
PS 欢迎在评论中联系我
Sergey Salnikov 给出了很好的答案。不过我想我可以添加另一个建议。
在我的 main.dart 文件中,我使用 MultiBlocProvider 创建我的所有块,以便在树的下方使用。像这样
Widget build(BuildContext context) {
return MultiBlocProvider(
providers: <BlocProvider<dynamic>>[
BlocProvider<OneBloc>(create: (_) => OneBloc()),
BlocProvider<TwoBloc>(create: (_) => TwoBloc()),
],
child: MaterialApp( // Rest of your app )
然后,当我需要在加载页面时调用事件时,在这种情况下,我想根据所选的列表块获取一些数据,并且我需要比 FutureBuilder 可以提供的更多选项,我简单地使用了 initState ();并调用了 bloc 提供者并添加了一个事件。
class _ExampleScreenState extends State<ExampleScreen> {
@override
void initState() {
super.initState();
BlocProvider.of<OneBloc>(context)
.add(FetchData);
}
之所以有效,是因为已经从根小部件提供了 bloc。