我正在构建tic-tak-toe脚趾应用程序,因此我决定学习Flutter的BLoC。我的BlocBuilder
小部件出现问题。
我考虑过。每当块构建器小部件监听的Cubit / Bloc发出新状态时,块构建器都会通过以下例程:
调用buildWhen
回调,将先前状态作为previous
参数,将新发出的状态作为current
参数。
如果buildWhen
回调返回true,则重新构建。
在重建过程中,调用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
回调。此解决方案的缺点是非常笨拙且不方便。
答案 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:
- 集团状态改变
- Flutter将窗口小部件标记为需要重建。
buildWhen将阻止由1触发的构建,而不是由2触发的生成。 在这种情况下,当语言改变时,整个小部件树可能 被重建,这就是为什么尽管生成了BlocBuilder buildWhen。
在您所处的情况下,根据所显示的少量代码,最好将整个Tic-Tac-Toe配置存储在该状态中,并使用BLOC事件对其进行更改。这样,您就不需要该buildWhen
条件。
OR (如果逻辑允许)在builder
函数中进行检查(这是BLOC最常用的解决方案)。
要回答您的问题(如果到目前为止尚不清楚:D):很遗憾,您不能依靠buildWhen
来过滤发送给builder
函数的状态类型
答案 1 :(得分:0)
能否请您检查 SetSlotSignGameState 是否扩展了抽象类 GameState