Flutter状态管理隔离插件isolated_bloc的使用

Flutter状态管理隔离插件isolated_bloc的使用

Motivation:

该库实现了将业务逻辑移动到单独的隔离区的想法。它在著名的Bloc库之上工作,没有改变它,而是对其进行了补充。

使用此库构建应用程序的主要思想是仅使用blocs创建业务逻辑,并运行所有这些逻辑在一个单独的隔离区。在这个相同的隔离区内,预计会创建大多数数据提供者,例如API和数据库。这将显著减轻主线程的负担,以实现响应式的UI。

然而,该库并不强制这样做,因为它简单地作为现有blocs的包装器工作。因此,只有部分blocs可以被移动到隔离区。在设计与数据源的交互时可能会出现不便,这些交互需要在主线程和bloc隔离区中都进行维护。

此外,该库允许blocs在不同的隔离区中工作。每个bloc可以分配一个在其内部创建的隔离区。

Limitations:

  1. 该库不支持与cubits一起工作。它只支持blocs。
  2. 由于实际创建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

1 回复

更多关于Flutter状态管理隔离插件isolated_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


isolated_bloc 是一个用于 Flutter 的状态管理插件,旨在将 BlocIsolate 结合使用,以便在独立线程中处理计算密集型任务,从而避免阻塞 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 类。IsolatedBlocisolated_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,并通过 IsolatedBlocBuilderIsolatedBlocListener 来监听状态变化。

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 会捕获并传递异常。你可以通过监听 IsolatedBloconError 来处理这些异常。

IsolatedBlocProvider(
  create: (context) => CounterBloc()..onError((error, stackTrace) {
    // 处理异常
    print('Error: $error');
  }),
  child: CounterPage(),
);

7. 结束 Isolate

当不再需要 Bloc 时,可以手动关闭 Isolate:

final bloc = context.isolatedBloc<CounterBloc, int>();
bloc.close();
回到顶部