Bloc:是否有可能产生 2 次相同的状态?

Lit*_*key 12 state handle flutter bloc

在登录视图中,如果用户在没有插入凭据的情况下点击登录按钮,则 LoginFailState 为 yield 并且视图对其做出反应。如果他再次点击,这个 LoginFailstate 又是 yield ,但视图不会对它做出反应。那么,有没有办法在相同的状态下产生更多倍的结果?

有一些代码可以更好地解释我的情况:

class LoginBloc extends Bloc<LoginEvent, LoginState> {
  @override
  LoginState get initialState => LoginUninitialized();

  @override
  Stream<LoginState> mapEventToState(LoginEvent event) {
    if (event is loginButtonPressed) {
      yield LoginFailState();
    }
  }
Run Code Online (Sandbox Code Playgroud)

看法:

 @override
  Widget build(BuildContext context) {
    return BlocBuilder(
      bloc: _loginBloc,
      builder: (BuildContext context, LoginState state) {
    if (state is LoginFail) {
        print ('Login fail');
    }
    return Column(
          ...
    )
Run Code Online (Sandbox Code Playgroud)

zab*_*son 14

如果您不扩展 Equitable,或者实现您自己的“==”逻辑使两个 LoginFailStates 相等,您可以收到“相同”状态的更新。

解决方案是在两者之间产生一个不同的状态,就像在 Bloc 示例中一样。

yield LoginLoading();
Run Code Online (Sandbox Code Playgroud)

每次点击登录按钮时都会调用它。Felangel 的 LoginBloc 示例。


Ole*_*hyn 12

问题

当您尝试发出与 Bloc 当前状态相等的新状态时,将不会发出新状态。

此行为是设计使然,将在此处进行讨论

当我说“比较相等”时,我的意思是两个状态对象的 == 运算符返回 true。

解决方案

有两种正确的方法:

  1. 你的状态类不应该扩展 Equatable。如果没有 Equatable,具有相同字段的同一类的两个对象将不会比较为相等,并且将始终发出此新状态。
  2. 有时你需要你的状态类来扩展 Equatable。identityHashCode(this)在这种情况下,只需将函数调用的结果添加到propsgetter 实现中即可:
class NeverEqualState extends Equatable {

  @override
  List<Object?> get props => [identityHashCode(this)];
}
Run Code Online (Sandbox Code Playgroud)

请注意,identityHashCode无论运算符 == 是否重载,我都可以使用它。相比之下,hashCode这里就行不通了。

警告

  1. 不要在 getter 实现中使用随机值List<Object> get props => [Random().nextDouble()];。随机变量是随机的,这意味着您仍然可能以极低的概率在序列中获得两个相等的值,从而破坏此解决方法。这是极不可能的,因此不可能重现和调试它。
  2. 您可以而且应该在get props实现中包含字段,但请记住,当所有字段比较相等时,对象也将比较相等。
  3. 在两个相等状态之间发出一些其他状态是可行的,但它会强制 BlocBuilder 重建部分 UI 和 BlocListener 来执行某些逻辑。这只是效率低下。

最后,为什么你想让一个状态类扩展 Equatable 但仍然不比较相等?当您的状态类实际上是层次结构的根时,可能需要这样做,其中一些后代需要正确实现 == 运算符,而另一些则需要永远不比较相等。这是示例:

class BaseMapState extends Equatable {
  const BaseMapState();

  @override
  List<Object?> get props => [];
}

class MapState extends BaseMapState {
  final Map<String, Report> reports;
  final Report? selectedReport;
  final LatLng? selectedPosition;
  final bool isLoadingNewReports;

  const MapState(
      {this.reports = const {},
      this.selectedReport,
      this.selectedPosition,
      this.isLoadingNewReports = false});

  @override
  List<Object?> get props => [
        ...reports.values,
        selectedReport,
        selectedPosition,
        isLoadingNewReports
      ];
}

class ErrorMapState extends BaseMapState {
  final String? error;

  const ErrorMapState(this.error);

  @override
  List<Object?> get props => [identityHashCode(this), error];
}

class NeedsAuthMapState extends ErrorMapState {
  const NeedsAuthMapState() : super('Authentication required');
}

class NoInternetMapState extends ErrorMapState {
  const NoInternetMapState() : super("No Internet connection");
}
Run Code Online (Sandbox Code Playgroud)


Kac*_*gut 7

默认情况下,当相同的状态将一个接一个地传递时,BLoC 模式不会发出状态。一种方法是在传递LoginFailState.

因此,在用户单击带有错误凭据的按钮后,传递的状态不会是:

LoginFailState()
LoginFailState()
Run Code Online (Sandbox Code Playgroud)

LoginFailState()
LoginEmptyState()
LoginFailState()
LoginEmptyState()
Run Code Online (Sandbox Code Playgroud)

这将使 UI 对它们中的每一个做出反应。

但我认为最好和最干净的解决方案是在通过LoadingState之前从 BLoC 传递LoginFailState()

您可以关注我最近撰写的有关此主题的博客文章


Boh*_*sko 6

如果您使用 Equitable 并尝试发出具有不同属性的同一 State 的两个相等实例,请确保覆盖propsarray。通过重写props数组,Equitable 将知道如何比较状态实例。

 class TablesLoadedState extends Equatable {
  final List<TableEntity> tablesList;

  TablesLoadedState(this.tablesList);

  @override
  List<Object> get props => [tablesList];
}
Run Code Online (Sandbox Code Playgroud)

因此,当 bloc 发出两个具有不同值的相同状态实例时,这些状态实例将传递给 BlocListener,并且 UI 将根据新数据进行更新。