You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 11:47:54