Flutter Bloc处理点击事件仅生效一次问题求助
我来帮你排查这个Bloc点击无响应的问题,核心原因其实和Equatable的状态对比逻辑有关:
问题根源
Bloc内部有个关键逻辑:如果新生成的状态和当前状态相等,就不会将这个状态发送给UI。而你的HandleDrawerMenuClick状态类没有正确实现Equatable的属性对比规则:
看你的HandleDrawerMenuClick代码:
class HandleDrawerMenuClick extends HomeState{ @override String toString()=>'HandleDrawerMenuClick'; }
它继承自HomeState,但没有给Equatable的props传递任何唯一标识。这意味着每次你yield HandleDrawerMenuClick()时,Equatable都会判定这两个实例是完全相等的(因为props是空列表,对比结果一致)。第一次点击时,初始状态是HomeState(),和HandleDrawerMenuClick不同,所以UI会响应;但第二次点击时,新状态和当前状态都是HandleDrawerMenuClick且被判定为相等,Bloc就会忽略这个状态更新,自然不会触发UI的打印逻辑。
修复方案
只需要给HandleDrawerMenuClick添加一个唯一属性,让Equatable能区分不同的状态实例即可。最简单的方式是加入时间戳:
class HandleDrawerMenuClick extends HomeState{ final DateTime timestamp; HandleDrawerMenuClick() : timestamp = DateTime.now(), super([DateTime.now()]); // 这里也可以直接传timestamp到父类props @override List<Object> get props => [timestamp]; @override String toString()=>'HandleDrawerMenuClick'; }
或者如果不需要额外数据,用随机值也能达到效果:
import 'dart:math'; class HandleDrawerMenuClick extends HomeState{ HandleDrawerMenuClick() : super([Random().nextInt(999999)]); @override List<Object> get props => super.props; @override String toString()=>'HandleDrawerMenuClick'; }
额外的优化建议
- 移除重复的MaterialApp:你的
App类已经是根MaterialApp的home,但MainPage的build方法又返回了一个新的MaterialApp,这会导致上下文嵌套混乱。建议直接返回Scaffold:
@override Widget build(BuildContext context) { return BlocBuilder<HomeEvent,HomeState>( bloc: _homeBloc, builder: (BuildContext context, HomeState state) { if (state is HandleDrawerMenuClick) { _onWidgetDidBuild(() { print("clicked on drawer menu"); }); } return Scaffold( body: Center( child: RaisedButton( child: Text('ddddddddd'), onPressed: () { _homeBloc.add(OnDrawerMenuClicked()); // 注意:新版Bloc用add替代dispatch }, ), ), ); }, ); }
- 更新Bloc API:你当前用的
dispatch是旧版API,最新的Bloc库已经改用add方法,建议同步更新。
这样修改后,每次点击按钮都会生成一个唯一的状态实例,Bloc会正确发送状态更新,你的控制台就能每次都打印出点击日志了。
内容的提问来源于stack exchange,提问作者DolDurma
相关产品推荐
相关产品推荐

