Flutter + Bloc 6.0.6。 BlocBuilder的“ builder”回调未提供与“ buildWhen”回调相同的状态

时间:2020-11-07 13:24:28

标签: flutter dart bloc flutter-bloc

我正在构建tic-tak-toe脚趾应用程序,因此我决定学习Flutter的BLoC。我的BlocBuilder小部件出现问题。

我考虑过。每当块构建器小部件监听的Cubit / Bloc发出新状态时,块构建器都会通过以下例程:

  1. 调用buildWhen回调,将先前状态作为previous参数,将新发出的状态作为current参数。

  2. 如果buildWhen回调返回true,则重新构建。

  3. 在重建过程中,调用builder回调将给定上下文作为context参数,将新发出的状态作为state参数。此回调返回我们返回的小部件。

因此,结论是current调用的buildWhen参数始终等于state调用的builder参数。但实际上不同:

BlocBuilder<GameCubit, GameState>(
      buildWhen: (previous, current) => current is SetSlotSignGameState && (current as SetSlotSignGameState).slotPosition == widget.pos,
      builder: (context, state) {
        var sign = (state as SetSlotSignGameState).sign;
        // Widget creation goes here...
      },
    );

builder回调中,它引发:

构建BlocBuilder (脏,状态: _BlocBuilderBaseState #dc100): 类型'GameState'不是类型转换中类型'SetSlotSignGameState'的子类型 相关的引起错误的小部件是: BlocBuilder

我发出GameCubit类中的状态的方法:

// [pos] is the position of the slot clicked
void setSlotSign(Vec2<int> pos) {

    // Some code

    emit(SetSlotSignGameState(/* Parameter representing the sign that is being placed in the slot*/, pos));

    // Some code

    emit(TurnChangeGameState());
}

简短说明状态类型。当用户在井字游戏网格中点击一个插槽并且该插槽为空时,将发出SetSlotSignGameState。因此,此状态意味着我们需要在某些插槽中更改符号。需要轮到下一位玩家时,会发出TurnChangeGameState

临时解决方案。现在,我通过将buildWhen回调中的状态保存在 widget的状态的私有字段中,然后在构建器中使用它来修复它。 BlocListener也有此问题,但是我可以将支票从listenWhen回调移到listen回调。此解决方案的缺点是非常笨拙且不方便。

2 个答案:

答案 0 :(得分:1)

buildWhen在初始状态下或当Flutter要求重建时被忽略(甚至没有被呼叫)

我创建了一个小“测试”以强调这一点:

import 'package:bloc/bloc.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

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

class BlocTestApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider<TestCubit>(
        // Create the TestCubit and call test() event right away
        create: (context) => TestCubit()..test(),
        child: BlocBuilder<TestCubit, String>(
          buildWhen: (previous, current) {
            print("Call buildWhen(previous: $previous, current: $current)");
            return false;
          },
          builder: (context, state) {
            print("Build $state");
            return Text(state);
          },
        ),
      ),
    );
  }
}

class TestCubit extends Cubit<String> {
  TestCubit() : super("Initial State");

  void test() {
    Future.delayed(Duration(seconds: 2), () {
      emit("Test State");
    });
  }
}

输出:

I/flutter (13854): Build Initial State
I/flutter (13854): Call buildWhen(previous: Initial State, current: Test State)

从输出中可以看出,无需调用buildWhen就可以立即建立初始状态。仅在状态更改buildWhen时检查。


其他参考

Flutter Bloc库(@felangel)的创建者也here概述了此行为:

这是预期的行为,因为有两个原因 要重建的BlocBuilder:

  1. 集团状态改变
  2. Flutter将窗口小部件标记为需要重建。

buildWhen将阻止由1触发的构建,而不是由2触发的生成。 在这种情况下,当语言改变时,整个小部件树可能 被重建,这就是为什么尽管生成了BlocBuilder buildWhen。


可能的解决方案

在您所处的情况下,根据所显示的少量代码,最好将整个Tic-Tac-Toe配置存储在该状态中,并使用BLOC事件对其进行更改。这样,您就不需要该buildWhen条件。

OR (如果逻辑允许)在builder函数中进行检查(这是BLOC最常用的解决方案)。

要回答您的问题(如果到目前为止尚不清楚:D):很遗憾,您不能依靠buildWhen来过滤发送给builder函数的状态类型

答案 1 :(得分:0)

能否请您检查 SetSlotSignGameState 是否扩展了抽象类 GameState