Flutter状态管理架构插件ilgiz_bloc的使用

Flutter状态管理架构插件ilgiz_bloc的使用

Flutter Bloc Package


使用 #

让我们看看如何使用BlocProviderCounterPage提供一个CounterCubit,并通过BlocBuilder响应状态变化。

counter_cubit.dart #

class CounterCubit extends Cubit<int> {
  CounterCubit() : super(0);

  void increment() => emit(state + 1);
  void decrement() => emit(state - 1);
}

main.dart #

void main() => runApp(CounterApp());

class CounterApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (_) => CounterCubit(),
        child: CounterPage(),
      ),
    );
  }
}

counter_page.dart #

class CounterPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Counter')),
      body: BlocBuilder<CounterCubit, int>(
        builder: (context, count) => Center(child: Text('$count')),
      ),
      floatingActionButton: Column(
        crossAxisAlignment: CrossAxisAlignment.end,
        mainAxisAlignment: MainAxisAlignment.end,
        children: <Widget>[
          FloatingActionButton(
            child: const Icon(Icons.add),
            onPressed: () => context.read<CounterCubit>().increment(),
          ),
          const SizedBox(height: 4),
          FloatingActionButton(
            child: const Icon(Icons.remove),
            onPressed: () => context.read<CounterCubit>().decrement(),
          ),
        ],
      ),
    );
  }
}

此时,我们已经成功将展示层与业务逻辑层分离。注意CounterPage小部件对用户点击按钮时发生的情况一无所知。该小部件只是通知CounterCubit用户按下了增加或减少按钮。

Bloc Widgets #

BlocBuilder #

BlocBuilder 是一个 Flutter 小部件,它需要一个bloc和一个builder函数。BlocBuilder会根据新的状态构建小部件。BlocBuilder类似于StreamBuilder,但具有更简单的 API 来减少样板代码的数量。builder 函数可能会被调用多次,并且应该是一个纯函数,根据状态返回一个 widget。

如果需要在状态变化时执行某些操作(如导航、显示对话框等),请查看BlocListener

如果省略了bloc参数,BlocBuilder将自动通过BlocProvider和当前的BuildContext进行查找。

BlocBuilder<BlocA, BlocAState>(
  builder: (context, state) {
    // 根据 BlocA 的状态返回小部件
  }
)

仅当需要提供一个作用域限于单个小部件且无法通过父级BlocProvider和当前BuildContext访问的小部件时,才指定bloc

BlocBuilder<BlocA, BlocAState>(
  bloc: blocA, // 提供本地 bloc 实例
  builder: (context, state) {
    // 根据 BlocA 的状态返回小部件
  }
)

为了对builder函数的调用进行细粒度控制,可以提供可选的buildWhenbuildWhen接受前一个 bloc 状态和当前 bloc 状态并返回一个布尔值。如果buildWhen返回true,则builder将被调用并重新构建小部件;如果返回false,则不会调用builder,也不会重新构建小部件。

BlocBuilder<BlocA, BlocAState>(
  buildWhen: (previousState, state) {
    // 返回 true/false 以确定是否要基于状态重新构建小部件
  },
  builder: (context, state) {
    // 根据 BlocA 的状态返回小部件
  }
)

BlocSelector #

BlocSelector 是一个 Flutter 小部件,类似于BlocBuilder,但它允许开发者通过选择基于当前 bloc 状态的新值来过滤更新。如果所选值未发生变化,则可以防止不必要的构建。所选值必须是不可变的,以便BlocSelector能够准确地确定是否应再次调用builder

如果省略了bloc参数,BlocSelector将自动通过BlocProvider和当前的BuildContext进行查找。

BlocSelector<BlocA, BlocAState, SelectedState>(
  selector: (state) {
    // 基于提供的状态返回所选状态。
  },
  builder: (context, state) {
    // 基于所选状态返回小部件。
  },
)

BlocProvider #

BlocProvider 是一个 Flutter 小部件,通过BlocProvider.of<T>(context)向其子项提供 bloc。它是作为依赖注入 (DI) 小部件使用的,以便为子树中的多个小部件提供单个 bloc 实例。

在大多数情况下,应该使用BlocProvider创建新的 bloc,这些 bloc 将可供子树中的其他部分使用。在这种情况下,由于BlocProvider负责创建 bloc,因此它将自动处理关闭。

BlocProvider(
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

默认情况下,BlocProvider会懒加载 bloc,这意味着当通过BlocProvider.of<BlocA>(context)查找 bloc 时,才会执行create

若要覆盖此行为并强制立即运行create,可以将lazy设置为false

BlocProvider(
  lazy: false,
  create: (BuildContext context) => BlocA(),
  child: ChildA(),
);

在某些情况下,可以使用BlocProvider向新的 widget 树部分提供现有的 bloc。这通常用于将现有 bloc 提供给新路由。在这种情况下,由于BlocProvider没有创建 bloc,因此不会自动关闭 bloc。

BlocProvider.value(
  value: BlocProvider.of<BlocA>(context),
  child: ScreenA(),
);

然后可以从ChildAScreenA检索BlocA

// 使用扩展
context.read<BlocA>();

// 不使用扩展
BlocProvider.of<BlocA>(context);

上述片段导致一次查找,小部件不会收到更改的通知。要检索实例并订阅后续状态更改,请使用:

// 使用扩展
context.watch<BlocA>();

// 不使用扩展
BlocProvider.of<BlocA>(context, listen: true);

此外,还可以使用context.select来检索状态的一部分,并仅在所选部分发生变化时作出反应。

final isPositive = context.select((CounterBloc b) => b.state >= 0);

上述代码只有在CounterBloc的状态从正变为负或反之时才会重建,并且功能上等同于使用BlocSelector

MultiBlocProvider #

MultiBlocProvider 是一个 Flutter 小部件,它可以将多个BlocProvider小部件合并为一个。
MultiBlocProvider 提高了可读性,并消除了嵌套多个BlocProvider的需要。
通过使用MultiBlocProvider,我们可以从:

BlocProvider<BlocA>(
  create: (BuildContext context) => BlocA(),
  child: BlocProvider<BlocB>(
    create: (BuildContext context) => BlocB(),
    child: BlocProvider<BlocC>(
      create: (BuildContext context) => BlocC(),
      child: ChildA(),
    )
  )
)

转换为:

MultiBlocProvider(
  providers: [
    BlocProvider<BlocA>(
      create: (BuildContext context) => BlocA(),
    ),
    BlocProvider<BlocB>(
      create: (BuildContext context) => BlocB(),
    ),
    BlocProvider<BlocC>(
      create: (BuildContext context) => BlocC(),
    ),
  ],
  child: ChildA(),
)

BlocListener #

BlocListener 是一个 Flutter 小部件,它接受一个BlocWidgetListener和一个可选的bloc,并在 bloc 的状态变化时调用listener。它应该用于需要在每次状态变化时发生的功能,例如导航、显示SnackBar、显示Dialog等。

listener对于每个状态变化只调用一次(不包括初始状态),不像BlocBuilder中的builder,并且是一个void函数。

如果省略了bloc参数,BlocListener将自动通过BlocProvider和当前的BuildContext进行查找。

BlocListener<BlocA, BlocAState>(
  listener: (context, state) {
    // 基于 BlocA 的状态执行操作
  },
  child: Container(),
)

仅当需要提供一个无法通过BlocProvider和当前BuildContext访问的 bloc 时,才指定bloc

BlocListener<BlocA, BlocAState>(
  bloc: blocA,
  listener: (context, state) {
    // 基于 BlocA 的状态执行操作
  }
)

为了对listener函数的调用进行细粒度控制,可以提供可选的listenWhenlistenWhen接受前一个 bloc 状态和当前 bloc 状态并返回一个布尔值。如果listenWhen返回true,则listener将被调用;如果返回false,则不会调用listener

BlocListener<BlocA, BlocAState>(
  listenWhen: (previousState, state) {
    // 返回 true/false 以确定是否调用 listener
  },
  listener: (context, state) {
    // 基于 BlocA 的状态执行操作
  },
  child: Container(),
)

MultiBlocListener #

MultiBlocListener 是一个 Flutter 小部件,它可以将多个BlocListener小部件合并为一个。
MultiBlocListener 提高了可读性,并消除了嵌套多个BlocListener的需要。
通过使用MultiBlocListener,我们可以从:

BlocListener<BlocA, BlocAState>(
  listener: (context, state) {},
  child: BlocListener<BlocB, BlocBState>(
    listener: (context, state) {},
    child: BlocListener<BlocC, BlocCState>(
      listener: (context, state) {},
      child: ChildA(),
    ),
  ),
)

转换为:

MultiBlocListener(
  listeners: [
    BlocListener<BlocA, BlocAState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocB, BlocBState>(
      listener: (context, state) {},
    ),
    BlocListener<BlocC, BlocCState>(
      listener: (context, state) {},
    ),
  ],
  child: ChildA(),
)

BlocConsumer #

BlocConsumer 暴露了builderlistener,以便响应新的状态。BlocConsumer类似于嵌套的BlocListenerBlocBuilder,但减少了样板代码的数量。BlocConsumer应该只在需要同时重建 UI 并对 bloc 的状态变化执行其他反应时使用。BlocConsumer需要一个必需的BlocWidgetBuilderBlocWidgetListener,以及可选的blocBlocBuilderConditionBlocListenerCondition

如果省略了bloc参数,BlocConsumer将自动通过BlocProvider和当前的BuildContext进行查找。

BlocConsumer<BlocA, BlocAState>(
  listener: (context, state) {
    // 基于 BlocA 的状态执行操作
  },
  builder: (context, state) {
    // 根据 BlocA 的状态返回小部件
  }
)

可以实现可选的listenWhenbuildWhen来进行更精细的控制,以决定何时调用listenerbuilderlistenWhenbuildWhen将在每次bloc状态变化时被调用。它们各自接受前一个state和当前state,并必须返回一个bool,以确定是否调用builder和/或listener函数。前一个state将在BlocConsumer初始化时设置为bloc的当前statelistenWhenbuildWhen是可选的,如果没有实现,它们将默认为true

BlocConsumer<BlocA, BlocAState>(
  listenWhen: (previous, current) {
    // 返回 true/false 以确定是否调用 listener
  },
  listener: (context, state) {
    // 基于 BlocA 的状态执行操作
  },
  buildWhen: (previous, current) {
    // 返回 true/false 以确定是否重建小部件
  },
  builder: (context, state) {
    // 根据 BlocA 的状态返回小部件
  }
)

RepositoryProvider #

RepositoryProvider 是一个 Flutter 小部件,它通过RepositoryProvider.of<T>(context)向其子项提供 repository。它是作为依赖注入 (DI) 小部件使用的,以便为子树中的多个小部件提供单个 repository 实例。BlocProvider应该用于提供 bloc,而RepositoryProvider应该仅用于 repository。

RepositoryProvider(
  create: (context) => RepositoryA(),
  child: ChildA(),
);

然后可以从ChildA检索Repository实例:

// 使用扩展
context.read<RepositoryA>();

// 不使用扩展
RepositoryProvider.of<RepositoryA>(context)

MultiRepositoryProvider #

MultiRepositoryProvider 是一个 Flutter 小部件,它可以将多个RepositoryProvider小部件合并为一个。
MultiRepositoryProvider 提高了可读性,并消除了嵌套多个RepositoryProvider的需要。
通过使用MultiRepositoryProvider,我们可以从:

RepositoryProvider<RepositoryA>(
  create: (context) => RepositoryA(),
  child: RepositoryProvider<RepositoryB>(
    create: (context) => RepositoryB(),
    child: RepositoryProvider<RepositoryC>(
      create: (context) => RepositoryC(),
      child: ChildA(),
    )
  )
)

转换为:

MultiRepositoryProvider(
  providers: [
    RepositoryProvider<RepositoryA>(
      create: (context) => RepositoryA(),
    ),
    RepositoryProvider<RepositoryB>(
      create: (context) => RepositoryB(),
    ),
    RepositoryProvider<RepositoryC>(
      create: (context) => RepositoryC(),
    ),
  ],
  child: ChildA(),
)

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

1 回复

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


ilgiz_bloc 是一个基于 flutter_bloc 的状态管理插件,旨在简化 Bloc 模式的使用并提供更简洁的 API。以下是如何使用 ilgiz_bloc 的基本指南。

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 ilgiz_bloc 依赖:

dependencies:
  flutter:
    sdk: flutter
  ilgiz_bloc: ^1.0.0  # 请使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 创建 Bloc

使用 ilgiz_bloc 创建 Bloc 非常简单。以下是一个简单的计数器示例:

import 'package:ilgiz_bloc/ilgiz_bloc.dart';

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

  [@override](/user/override)
  Stream<int> mapEventToState(CounterEvent event) async* {
    if (event is IncrementEvent) {
      yield state + 1;
    } else if (event is DecrementEvent) {
      yield state - 1;
    }
  }
}

abstract class CounterEvent {}

class IncrementEvent extends CounterEvent {}

class DecrementEvent extends CounterEvent {}

3. 使用 BlocBuilder 和 BlocProvider

在 Flutter 应用中使用 BlocProviderBlocBuilder 来管理状态:

import 'package:flutter/material.dart';
import 'package:ilgiz_bloc/ilgiz_bloc.dart';
import 'counter_bloc.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider(
        create: (context) => CounterBloc(),
        child: CounterPage(),
      ),
    );
  }
}

class CounterPage extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    final CounterBloc counterBloc = BlocProvider.of<CounterBloc>(context);

    return Scaffold(
      appBar: AppBar(title: Text('Counter App')),
      body: Center(
        child: BlocBuilder<CounterBloc, int>(
          builder: (context, count) {
            return Text('Count: $count', style: TextStyle(fontSize: 24));
          },
        ),
      ),
      floatingActionButton: Column(
        mainAxisAlignment: MainAxisAlignment.end,
        children: [
          FloatingActionButton(
            onPressed: () => counterBloc.add(IncrementEvent()),
            child: Icon(Icons.add),
          ),
          SizedBox(height: 10),
          FloatingActionButton(
            onPressed: () => counterBloc.add(DecrementEvent()),
            child: Icon(Icons.remove),
          ),
        ],
      ),
    );
  }
}
回到顶部