【问题标题】:Flutter + Bloc 6.0.6. BlocBuilder's "builder" callback isn't provided with the same state as the "buildWhen" callback颤振 + Bloc 6.0.6。 BlocBuilder 的“builder”回调未提供与“buildWhen”回调相同的状态
【发布时间】:2021-02-19 23:37:15
【问题描述】:

我正在构建一个井字游戏应用程序,我决定一起学习 BLoC for Flutter。 BlocBuilder 小部件有问题。

在我看来。每次 bloc 构建器小部件侦听的 Cubit/Bloc 发出新状态时,bloc 构建器都会执行此例程:

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

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

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

所以结论是buildWhen调用的current参数总是等于builder调用的state参数。但实际上它是不同的:

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(dirty, state: _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's 状态 的私有字段中,然后从构建器中使用它来修复它。 BlocListener 也有这个问题,但我可以将检查从 listenWhen 回调移动到 listen 回调。这种解决方案的缺点是非常不优雅和不方便。

【问题讨论】:

  • 在您的代码中出现语法错误:buildWhen: (previous, current) =&gt; current is SetSlotSignGameState &amp;&amp; (current as SetSlotSignGameState).slotPosition == widget.pos) &lt;-- THIS BRACKET HAS NO PAIR,。那是冒险吗?
  • jorjdaniel,是的,这是我从代码编辑器中复制代码的错误,项目中的原始代码没有这个错误。很抱歉让您对此感到困惑。我将编辑删除错字的问题。

标签: flutter dart bloc flutter-bloc


【解决方案1】:

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

【讨论】:

  • GameState 类在我的例子中不是抽象的。他们都扩展了它,它位于州层次结构的顶部。 GameState 也扩展了 Equatable。所有具有属性的子类都将它们添加到“props”getter 中。
【解决方案2】:

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 被重建的原因 构建时间。


可能的解决方案

在您的情况下,根据您透露的小代码,最好将整个井字游戏配置存储在状态中并使用 BLOC 事件来更改它。这样你就不需要buildWhen这个条件了。

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

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

【讨论】:

    猜你喜欢
    • 2021-01-05
    • 2019-12-28
    • 2020-07-20
    • 1970-01-01
    • 2021-03-19
    • 1970-01-01
    • 1970-01-01
    • 2020-06-15
    • 2020-02-07
    相关资源
    最近更新 更多