Flutter状态管理架构插件bloc_toolkit的使用
Flutter状态管理架构插件 bloc_toolkit
的使用
bloc_toolkit
是一个用于在Flutter应用中高效灵活地进行状态管理的插件,基于Bloc模式。它旨在简化应用程序开发,提供高级功能如加载、重新加载、更新和初始化数据。
DataBloc
DataBloc
类是该包的核心。这是一个通用类,提供了管理数据源状态的一整套工具,并设计为实现业务逻辑的基础类。
创建你的DataBloc
class AnimalBloc extends DataBloc<String, String> {
AnimalBloc({required AnimalRepository animalRepository})
: _animalRepository = animalRepository;
final AnimalRepository _animalRepository;
[@override](/user/override)
FutureOr<String> loadData(DataS<String> oldState, LoadDataE<String> event) {
return _animalRepository.getAnimal(event.params!);
}
}
构建你的Widgets
BlocProvider(
create: (_) => AnimalBloc(animalRepository: AnimalRepository()),
child: BlocConsumer<AnimalBloc, DataS<String>>(
listener: (context, state) {
if (state is ErrorS<String>) {
_showSnackBar(context, 'Loading animal error: ${state.error}');
}
},
builder: (context, state) {
if (state is UnloadedDataS<String>) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Animal not loaded'),
ElevatedButton(
onPressed: () {
context.read<AnimalBloc>().add(const LoadDataE(params: 'some args'));
},
child: const Text('Load Animal'),
),
],
);
}
if (state is LoadingDataS<String>) {
return const Text('Loading animal...');
}
if (state is LoadedDataS<String, String>) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(state.data),
ElevatedButton(
onPressed: () {
context.read<AnimalBloc>().add(const ReloadDataE(params: 'some args'));
},
child: const Text('Reload Animal'),
),
],
);
}
if (state is ReloadingDataS<String, String>) {
return const Text('Reloading animal...');
}
return const SizedBox();
},
),
)
添加事件
final animalBloc = context.read<AnimalBloc>();
// 加载数据
animalBloc.add(const LoadDataE(params: 'some args'));
// 或者 初始化数据
animalBloc.add(InitializeDataE('dog'))
// 然后你可以
animalBloc.add(const ReloadDataE(params: 'some args'))
// 或者 更新数据
animalBloc.add(UpdateDataE((currentData) => 'cat'));
状态
DataBloc
支持以下几种状态:
-
基础状态(Base States)
abstract DataS
: 所有状态的基础。abstract IdleS
: 当没有事情发生时的状态。abstract LoadingS
: 数据正在加载或重新加载时的状态。abstract ErrorS
: 发生错误时的状态。
-
数据未加载状态(Data unloaded states)
UnloadedDataS
: 初始状态,没有任何数据被加载。LoadingDataS
: 数据正在加载时的状态。LoadingDataErrorS
: 数据加载时发生错误的状态。
-
数据已加载状态(Data loaded states)
LoadedDataS
: 数据成功加载或初始化成功的状态。ReloadingDataS
: 数据正在重新加载时的状态。ReloadingDataErrorS
: 数据重新加载时发生错误的状态。
错误处理
默认情况下,所有在数据加载期间抛出的错误都会转换为 DataException
,可以在 LoadingDataErrorS
或 ReloadingDataErrorS
状态中接收。
如果你想在 BlocObserver.onError
中处理这些错误,可以通过覆盖 overridedOnLoadingError
和 overridedOnReloadingError
方法来改变这种行为。
void _$onLoadingError(
DataException error,
UnloadedDataS<String> state,
Emitter<DataS<String>> emit, {
String? params,
}) {
if (error is UnhandledDataException) {
throw error;
}
emit(LoadingDataErrorS(error, params: params));
emit(const UnloadedDataS());
}
void _$onReloadingError(
DataException error,
LoadedS<String, String> state,
Emitter<DataS<String>> emit, {
String? params,
}) {
if (error is UnhandledDataException) {
throw error;
}
emit(ReloadingDataErrorS(state, error, params: params));
emit(LoadedDataS(state.data, params: state.params));
}
class AnimalBloc extends DataBloc<String, String> {
AnimalBloc({
required AnimalRepository animalRepository,
super.overridedOnLoadingError = _$onLoadingError,
super.overridedOnReloadingError = _$onReloadingError,
})
}
然后你可以在 BlocObserver.onError
中处理它们:
[@override](/user/override)
void onError(BlocBase bloc, Object error, StackTrace stackTrace) {
super.onError(bloc, error, stackTrace);
if (error is UnhandledDataException) {
final originError = error.error;
final originStackTrace = error.stackTrace;
// send to sentry...
}
}
ListBloc 和 SelectBloc
ListBloc
提供了对列表进行排序和过滤的功能。而 SelectBloc
实现了从列表中选择项目的便捷操作。
示例代码
下面是一个完整的示例,展示了如何使用 bloc_toolkit
插件:
import 'dart:async';
import 'package:bloc_toolkit/bloc_toolkit.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:logger/logger.dart';
void main() {
final logger = Logger();
Bloc.observer = SimpleBlocObserver(logger);
runApp(const MyApp());
}
class AnimalBloc extends DataBloc<String, String> {
AnimalBloc({required AnimalRepository animalRepository})
: _animalRepository = animalRepository;
final AnimalRepository _animalRepository;
[@override](/user/override)
FutureOr<String> loadData(DataS<String> oldState, LoadDataE<String> event) {
return _animalRepository.getAnimal(event.params!);
}
}
class SimpleBlocObserver extends BlocObserver {
SimpleBlocObserver(this._logger);
final Logger _logger;
[@override](/user/override)
void onChange(BlocBase bloc, Change change) {
super.onChange(bloc, change);
final nextState = change.nextState;
if (nextState is ErrorS) {
final error = nextState.error;
if (error is UnhandledDataException) {
_logger.f('UnhandledDataException',
error: error.error, stackTrace: error.stackTrace);
//TODO: send to analytics
}
}
}
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
[@override](/user/override)
Widget build(BuildContext context) {
return const MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({
super.key,
});
void _showSnackBar(BuildContext context, String text) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
backgroundColor: Colors.red,
content: Text(text),
duration: const Duration(milliseconds: 1000),
));
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: BlocProvider(
create: (_) => AnimalBloc(animalRepository: AnimalRepository()),
child: BlocConsumer<AnimalBloc, DataS<String>>(
listener: (context, state) {
if (state is ReloadingDataErrorS<String, String>) {
_showSnackBar(
context, 'Reloading animal error: ${state.data}');
} else if (state is LoadingDataErrorS<String, String>) {
_showSnackBar(
context, 'Loading animal error: ${state.error}');
}
},
builder: (context, state) {
if (state is LoadingDataS<String>) {
return const Text('Loading animal...');
}
if (state is UnloadedS<String>) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
const Text('Animal not loaded'),
ElevatedButton(
onPressed: () {
context
.read<AnimalBloc>()
.add(const LoadDataE(params: 'some args'));
},
child: const Text('Load Animal'),
),
],
);
}
if (state is LoadedDataS<String, String>) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(state.data),
ElevatedButton(
onPressed: () {
context
.read<AnimalBloc>()
.add(const ReloadDataE(params: 'some args'));
},
child: const Text('Reload Animal'),
),
],
);
}
if (state is ReloadingDataS<String, String>) {
return const Text('Reloading animal...');
}
return const SizedBox();
},
),
),
),
);
}
}
更多关于Flutter状态管理架构插件bloc_toolkit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter状态管理架构插件bloc_toolkit的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是一个关于如何在Flutter项目中使用bloc_toolkit
进行状态管理的代码示例。bloc_toolkit
是一个集合了多个Bloc相关工具和扩展的Flutter包,它可以帮助我们更方便地进行状态管理。然而,需要注意的是,bloc_toolkit
本身并不是状态管理架构的核心库,它更多地是对bloc
库的增强。因此,我们通常还是使用bloc
库来进行状态管理,而bloc_toolkit
则提供一些便利的工具。
在这个示例中,我们将使用bloc
库进行状态管理,并展示如何利用bloc_toolkit
中的一些便利功能。
首先,确保在pubspec.yaml
文件中添加必要的依赖:
dependencies:
flutter:
sdk: flutter
bloc: ^8.0.0 # 请根据需要调整版本号
flutter_bloc: ^8.0.0 # 请根据需要调整版本号
bloc_toolkit: ^0.0.1 # 假设bloc_toolkit的版本号为0.0.1,请根据实际情况调整
然后,我们创建一个简单的计数器应用来展示如何使用这些库。
1. 创建CounterEvent类
import 'package:equatable/equatable.dart';
abstract class CounterEvent extends Equatable {
const CounterEvent();
@override
List<Object> get props => [];
}
class IncrementEvent extends CounterEvent {}
class DecrementEvent extends CounterEvent {}
2. 创建CounterState类
import 'package:equatable/equatable.dart';
class CounterState extends Equatable {
final int count;
const CounterState({required this.count});
@override
List<Object> get props => [count];
}
3. 创建CounterBloc类
import 'package:bloc/bloc.dart';
import 'package:bloc_toolkit/bloc_toolkit.dart'; // 导入bloc_toolkit
import 'counter_event.dart';
import 'counter_state.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterState(count: 0)) {
on<IncrementEvent>((event, emit) => emit(state.copyWith(count: state.count + 1)));
on<DecrementEvent>((event, emit) => emit(state.copyWith(count: state.count - 1)));
}
}
在这个例子中,我们使用了bloc_toolkit
,但实际上在这个简单的Bloc实现中并没有直接用到bloc_toolkit
的特定功能。不过,bloc_toolkit
可能提供了一些扩展函数或工具,可以简化Bloc的创建和管理。例如,如果bloc_toolkit
提供了简化的Bloc创建方式或状态转换工具,我们可以在这里使用它们。但由于bloc_toolkit
的具体API和功能可能随着版本而变化,这里我们主要展示基本的Bloc实现。
4. 使用CounterBloc在UI中
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'counter_bloc.dart';
import 'counter_event.dart';
void main() {
runApp(BlocProvider<CounterBloc>(
create: (_) => CounterBloc(),
child: MyApp(),
));
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Counter')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text('You have pushed the button this many times:'),
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text(
'${state.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(IncrementEvent()),
tooltip: 'Increment',
child: Icon(Icons.add),
),
SizedBox(height: 10),
FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(DecrementEvent()),
tooltip: 'Decrement',
child: Icon(Icons.remove),
),
],
),
);
}
}
在这个例子中,我们使用了BlocProvider
来提供CounterBloc
实例,并在UI中使用BlocBuilder
来监听状态变化。同时,我们使用context.read<CounterBloc>().add(...)
来分发事件。
请注意,这个示例中并没有直接使用bloc_toolkit
的特定功能,因为bloc_toolkit
通常提供的是对Bloc库的扩展和便利工具。如果bloc_toolkit
提供了特定的功能,比如简化的Bloc创建、状态转换或调试工具,你可以根据bloc_toolkit
的文档来集成这些功能。由于bloc_toolkit
的具体API和功能可能随着版本而变化,请参考最新的bloc_toolkit
文档以获取更多信息和示例。