Flutter状态管理隔离插件isolated_bloc的使用
Flutter状态管理隔离插件isolated_bloc的使用
Motivation:
该库实现了将业务逻辑移动到单独的隔离区的想法。它在著名的Bloc库之上工作,没有改变它,而是对其进行了补充。
使用此库构建应用程序的主要思想是仅使用blocs创建业务逻辑,并运行所有这些逻辑在一个单独的隔离区。在这个相同的隔离区内,预计会创建大多数数据提供者,例如API和数据库。这将显著减轻主线程的负担,以实现响应式的UI。
然而,该库并不强制这样做,因为它简单地作为现有blocs的包装器工作。因此,只有部分blocs可以被移动到隔离区。在设计与数据源的交互时可能会出现不便,这些交互需要在主线程和bloc隔离区中都进行维护。
此外,该库允许blocs在不同的隔离区中工作。每个bloc可以分配一个在其内部创建的隔离区。
Limitations:
- 该库不支持与cubits一起工作。它只支持blocs。
- 由于实际创建blocs的隔离区不同于主线程,因此通过构造函数参数传递复杂依赖项的DI通常将停止工作。不基于上下文的DI需要在blocs操作的隔离区中设置。
Usage
Pubspec
dependencies:
isolated_bloc: ^1.3.0
Transformation of an existing bloc
在现有的应用中,将一个bloc转换为在隔离区中运行的bloc,你只需要添加三行代码并更改现有bloc的类名。
从:
class CounterBloc extends Bloc<CounterBlocEvent, CounterBlocState> {
...
}
到:
import 'package:isolated_bloc/isolated_bloc.dart';
class CounterBloc extends IsolatedBloc<CounterBlocEvent, CounterBlocState> {
CounterBloc() : super(() => _CounterBloc());
}
class _CounterBloc extends Bloc<CounterBlocEvent, CounterBlocState> {
...
}
由于在Web上不支持隔离区,因此对于Web使用一个简单的空包装器来包装bloc,这个包装器本身不做任何事情。
DI
为了简化将DI转移到隔离区,库提供了一个特殊的消息。以下是一个配置GetIt的例子。
final DI = GetIt.instance;
final IsolatedDI = GetIt.instance;
...
class SetupDIMessageToIsolate extends CallableMessageToIsolate {
void call() {
final dio = Dio();
IsolatedDI.registerSingleton(dio);
final api = ApiClient(dio);
IsolatedDI.registerSingleton(api);
}
}
...
if (kIsWeb) {
final dio = Dio();
DI.registerSingleton(dio);
final api = ApiClient(dio);
DI.registerSingleton(api);
} else {
IsolatedBloc.isolatesDispatcher.isolate().sendMessage(SetupDIMessageToIsolate());
}
...
class OriginalRelativesBloc extends Bloc<RelativesBlocEvent, RelativesBlocState> {
late final _api = kIsWeb ? DI<ApiClient>() : IsolatedDI<ApiClient>();
...
}
Working with multiple isolates
默认情况下,所有块都在一个隔离区中运行。这种设置不需要任何额外的配置。
然而,每个块可以分配一个在其内运行的隔离区。库中的隔离区由名称区分。默认隔离区名称为IsolatesDispatcher.kDefaultIsolateName
。要创建一个新的隔离区,只需在创建块时指定其名称即可。
class CounterBloc extends IsolatedBloc<CounterBlocEvent, CounterBlocState> {
CounterBloc({required String isolateName})
: super(() => _CounterBloc(), isolateName: isolateName);
}
...
BlocProvider(create: (BuildContext context) => CounterBloc(isolateName: _isolateName)),
另外,你可以显式地获取隔离区,例如,在其中配置DI(依赖注入):
IsolatedBloc.isolatesDispatcher.isolate(isolateName: _isolateName);
如果不存在具有该名称的隔离区,将会被创建。
当隔离区不再需要时,它可以被移除:
IsolatedBloc.isolatesDispatcher.removeIsolate(isolateName: _isolateName);
如果没有在任何这些情况中指定隔离区名称,则将使用默认隔离区。这个默认隔离区不能被移除。
Demo
示例演示了原始(Web)bloc和隔离区中的bloc在应用性能上的差异。示例显著且长时间地加载CPU,这在真实应用中很少发生,但它允许你清楚地看到UI性能与业务逻辑操作的解耦。
完整示例代码
以下是一个完整的示例代码,展示如何使用isolated_bloc
插件。
import 'package:flutter/material.dart';
import 'package:get_it/get_it.dart';
import 'package:isolated_bloc/isolated_bloc.dart';
import 'package:dio/dio.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
[@override](/user/override)
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final DI = GetIt.instance;
final IsolatedDI = GetIt.instance;
[@override](/user/override)
void initState() {
super.initState();
if (!kIsWeb) {
IsolatedBloc.isolatesDispatcher.isolate().sendMessage(SetupDIMessageToIsolate());
}
}
[@override](/user/override)
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('isolated_bloc Example')),
body: Center(
child: BlocProvider(
create: (context) => CounterBloc(isolateName: kIsWeb ? null : 'counter_isolate'),
child: CounterWidget(),
),
),
);
}
}
class CounterBloc extends IsolatedBloc<CounterBlocEvent, CounterBlocState> {
CounterBloc({String? isolateName}) : super(() => _CounterBloc(), isolateName: isolateName);
[@override](/user/override)
Stream<CounterBlocState> mapEventToState(CounterBlocEvent event) async* {
// Implement your state transition logic here
}
}
class _CounterBloc extends Bloc<CounterBlocEvent, CounterBlocState> {
int _count = 0;
[@override](/user/override)
CounterBlocState get initialState => InitialCounterState();
[@override](/user/override)
Stream<CounterBlocState> mapEventToState(CounterBlocEvent event) async* {
if (event is IncrementEvent) {
yield IncrementedCounterState(++_count);
} else if (event is DecrementEvent) {
yield DecrementedCounterState(--_count);
}
}
}
class CounterWidget extends StatelessWidget {
[@override](/user/override)
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: ${BlocProvider.of<CounterBloc>(context).state.count}'),
ElevatedButton(
onPressed: () {
BlocProvider.of<CounterBloc>(context).add(IncrementEvent());
},
child: Text('Increment'),
),
ElevatedButton(
onPressed: () {
BlocProvider.of<CounterBloc>(context).add(DecrementEvent());
},
child: Text('Decrement'),
),
],
);
}
}
class CounterBlocEvent {}
class CounterBlocState {}
class InitialCounterState extends CounterBlocState {
int get count => 0;
}
class IncrementedCounterState extends CounterBlocState {
final int count;
IncrementedCounterState(this.count);
}
class DecrementedCounterState extends CounterBlocState {
final int count;
DecrementedCounterState(this.count);
}
class IncrementEvent extends CounterBlocEvent {}
class DecrementEvent extends CounterBlocEvent {}
class SetupDIMessageToIsolate extends CallableMessageToIsolate {
[@override](/user/override)
void call() {
final dio = Dio();
IsolatedDI.registerSingleton(dio);
final api = ApiClient(dio);
IsolatedDI.registerSingleton(api);
}
}
class ApiClient {
final Dio dio;
ApiClient(this.dio);
}
更多关于Flutter状态管理隔离插件isolated_bloc的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter状态管理隔离插件isolated_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
isolated_bloc
是一个用于 Flutter 的状态管理插件,旨在将 Bloc
与 Isolate
结合使用,以便在独立线程中处理计算密集型任务,从而避免阻塞 UI 线程。这个插件非常适合需要执行耗时操作(如复杂的计算、文件读写、网络请求等)的应用场景。
以下是 isolated_bloc
的基本使用步骤:
1. 添加依赖
首先,在 pubspec.yaml
文件中添加 isolated_bloc
的依赖:
dependencies:
flutter:
sdk: flutter
bloc: ^8.0.0
isolated_bloc: ^1.0.0
然后执行 flutter pub get
安装依赖。
2. 创建 Bloc 类
你需要创建一个继承自 IsolatedBloc
的 Bloc 类。IsolatedBloc
是 isolated_bloc
提供的基类,它与普通的 Bloc
类似,但可以在独立的 Isolate 中执行。
import 'package:isolated_bloc/isolated_bloc.dart';
import 'package:bloc/bloc.dart';
class CounterEvent {}
class IncrementEvent extends CounterEvent {}
class CounterBloc extends IsolatedBloc<CounterEvent, int> {
CounterBloc() : super(0);
@override
Future<void> handle(CounterEvent event, Emitter<int> emit) async {
if (event is IncrementEvent) {
// 模拟一些计算密集型操作
await Future.delayed(Duration(seconds: 1));
emit(state + 1);
}
}
}
3. 创建 Isolated Bloc Provider
IsolatedBloc
需要在一个独立的 Isolate 中运行,因此你需要使用 IsolatedBlocProvider
来管理和提供 Bloc 实例。
import 'package:flutter/material.dart';
import 'package:isolated_bloc/isolated_bloc.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: IsolatedBlocProvider(
create: (context) => CounterBloc(),
child: CounterPage(),
),
);
}
}
4. 在 UI 中使用 Isolated Bloc
你可以在 UI 中使用 IsolatedBlocProvider
提供的 Bloc,并通过 IsolatedBlocBuilder
或 IsolatedBlocListener
来监听状态变化。
import 'package:flutter/material.dart';
import 'package:isolated_bloc/isolated_bloc.dart';
class CounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final bloc = context.isolatedBloc<CounterBloc, int>();
return Scaffold(
appBar: AppBar(title: Text('Isolated Bloc Example')),
body: Center(
child: IsolatedBlocBuilder<CounterBloc, int>(
builder: (context, state) {
return Text('Count: $state');
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
bloc.add(IncrementEvent());
},
child: Icon(Icons.add),
),
);
}
}
5. 处理 Bloc 生命周期
IsolatedBlocProvider
会自动管理 Bloc 的生命周期。当 IsolatedBlocProvider
被销毁时,与之关联的 Bloc 也会被关闭。
6. 处理 Isolate 异常
如果 Isolate 中发生异常,IsolatedBloc
会捕获并传递异常。你可以通过监听 IsolatedBloc
的 onError
来处理这些异常。
IsolatedBlocProvider(
create: (context) => CounterBloc()..onError((error, stackTrace) {
// 处理异常
print('Error: $error');
}),
child: CounterPage(),
);
7. 结束 Isolate
当不再需要 Bloc 时,可以手动关闭 Isolate:
final bloc = context.isolatedBloc<CounterBloc, int>();
bloc.close();