Flutter中借助Bloc在onDismissable事件里实现收藏切换
How to Toggle Favorite Items Using Bloc (Without setState)
我懂你现在的困扰——单独加收藏、删收藏都没问题,但同一个按钮切换状态就失灵了。咱们用Bloc的思路来搞定这个切换逻辑,全程不用setState,还能保证状态的一致性。
1. 新增一个「切换收藏」的事件
先在master_events.dart里加个专门处理切换的事件类,让Bloc能识别这个操作:
class MasterToggleFavorite extends MasterEvents { final YoutubeVideo youtubeVideo; MasterToggleFavorite(this.youtubeVideo); }
2. 在Bloc里实现切换逻辑
修改master_bloc.dart,先写处理切换的核心方法,再把它加到事件处理分支里:
首先添加切换逻辑的方法:
Stream<MasterState> _toggleFavorite(MasterToggleFavorite event) async* { final targetVideo = event.youtubeVideo; // 检查当前视频是否已在收藏列表中 final existingIndex = state.favorites.indexWhere( (favItem) => favItem.videoId == targetVideo.videoId ); if (existingIndex != -1) { // 已收藏:从列表移除 final updatedFavorites = List<YoutubeVideo>.from(state.favorites); updatedFavorites.removeAt(existingIndex); yield state.copyWith(favorites: updatedFavorites); } else { // 未收藏:添加到列表 final updatedFavorites = List<YoutubeVideo>.from(state.favorites); // 用copyWith创建新实例(保证不可变性) final newFavorite = targetVideo.copyWith(isFavorite: true); updatedFavorites.add(newFavorite); yield state.copyWith(favorites: updatedFavorites); } }
然后在mapEventToState里注册这个事件:
@override Stream<MasterState> mapEventToState(MasterEvents event) async* { // ... 保留原有其他事件的处理分支 ... else if (event is MasterToggleFavorite) { yield* _toggleFavorite(event); } }
3. 修正按钮的点击与渲染逻辑
把按钮的onPressed改成触发新的切换事件,同时从Bloc状态里判断是否已收藏(别直接用item的isFavorite,避免状态不一致):
// 注意要先拿到Bloc的当前状态,比如通过BlocBuilder获取 BlocBuilder<MasterBloc, MasterState>( builder: (context, state) { return CupertinoButton( padding: EdgeInsets.zero, minSize: 30, onPressed: () { context.read<MasterBloc>().add(MasterToggleFavorite(item)); }, child: CircleContainer( child: Icon( state.favorites.any((fav) => fav.videoId == item.videoId) ? Icons.favorite : Icons.favorite_border, color: Colors.white, ), size: 35, ), ); }, )
4. 优化YoutubeVideo的不可变性(关键)
你的YoutubeVideo现在是可变的,这不符合Bloc状态不可变的设计原则,容易导致状态混乱。把它改成不可变类:
class YoutubeVideo { final String videoId, title, description, banner; final bool isFavorite; // 改成final,禁止直接修改 YoutubeVideo({ @required this.videoId, @required this.title, @required this.description, @required this.banner, this.isFavorite = false, // 默认未收藏 }); // 添加copyWith方法,方便创建新实例修改状态 YoutubeVideo copyWith({bool isFavorite}) { return YoutubeVideo( videoId: this.videoId, title: this.title, description: this.description, banner: this.banner, isFavorite: isFavorite ?? this.isFavorite, ); } }
为什么之前的切换失效?
之前的问题根源在于:
- 直接修改
item.isFavorite但没同步到Bloc状态,导致UI和真实状态不一致 - 切换逻辑分散在UI层,没有统一交给Bloc处理,容易出现判断错误
现在的方案把所有状态逻辑都收拢在Bloc里,UI只负责触发事件和根据Bloc状态渲染,完全符合Bloc的设计理念,也彻底告别了setState。
内容的提问来源于stack exchange,提问作者Luciano
相关产品推荐
相关产品推荐

