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

Flutter开发中ListView.builder内Bloc事件仅触发一次问题咨询

问题根源

  • 所有列表项共用同一个UsersWatcherBloc实例,Bloc单次只能维护一份状态,新事件触发的请求结果会直接覆盖之前的状态,所以看起来仅一次事件生效
  • 你在itemBuilder中直接触发Bloc事件属于错误写法,build方法会因页面重绘多次触发,容易造成重复请求
  • 现有UsersWatcherState仅支持存储单份用户查询结果,无法同时保存多个userId对应的用户数据

解决方案

方案1:单Bloc缓存多用户数据(适用列表项多、重复userId多的场景)

改造Bloc的状态结构,用Map缓存每个userId对应的加载状态和数据,避免状态互相覆盖:

  1. 重新定义State结构
part of 'users_watcher_bloc.dart';

@freezed
abstract class UsersWatcherState with _$UsersWatcherState {
  const factory UsersWatcherState({
    // key为userId,value为对应用户的加载状态
    required Map<String, UserLoadState> userStateMap,
  }) = _UsersWatcherState;

  factory UsersWatcherState.initial() => const UsersWatcherState(userStateMap: {});
}

// 定义单用户的加载状态
@freezed
abstract class UserLoadState with _$UserLoadState {
  const factory UserLoadState.loadInProgress() = _LoadInProgress;
  const factory UserLoadState.loadFailure(Failure f) = _LoadFailure;
  const factory UserLoadState.empty() = _Empty;
  const factory UserLoadState.loadSuccess(User user) = _LoadSuccess;
}
  1. 改造Bloc逻辑
@override
Stream<UsersWatcherState> mapEventToState(
  UsersWatcherEvent event,
) async* {
  yield* event.map(
    watchAllUsers: (e) async* {
      final userId = e.uId;
      // 先标记当前用户为加载中,不影响其他用户的状态
      yield state.copyWith(
        userStateMap: Map.from(state.userStateMap)
          ..[userId] = const UserLoadState.loadInProgress(),
      );
      // 发起请求
      yield* _iElearningRepository.watchAllUsers(userId).map(
            (failureOrUsers) => failureOrUsers.fold(
              (f) => state.copyWith(
                userStateMap: Map.from(state.userStateMap)
                  ..[userId] = UserLoadState.loadFailure(f),
              ),
              (users) {
                final userState = users.isEmpty
                    ? const UserLoadState.empty()
                    : UserLoadState.loadSuccess(users.first);
                return state.copyWith(
                  userStateMap: Map.from(state.userStateMap)
                    ..[userId] = userState,
                );
              },
            ),
          );
    },
  );
}
  1. 改造列表项逻辑
itemBuilder: (context, index) {
  final question = state.questions.get(index);
  final userId = question.userId.getorCrash();
  final bloc = context.read<UsersWatcherBloc>();
  // 仅无缓存时触发请求,用addPostFrameCallback避免在build过程中修改状态
  if (!bloc.state.userStateMap.containsKey(userId)) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      bloc.add(UsersWatcherEvent.watchAllUsers(userId));
    });
  }
  // 只监听当前userId对应的状态变化
  return BlocBuilder<UsersWatcherBloc, UsersWatcherState>(
    builder: (context, state) {
      final currentUserState = state.userStateMap[userId];
      // 根据currentUserState渲染对应UI即可
      return Container(
        color: Colors.white,
        // 原有布局代码
      );
    },
  );
}

方案2:每个列表项独立Bloc实例(适用列表项少、实现要求简单的场景)

  1. 把UsersWatcherBloc在injectable中注册为工厂模式,保证每次调用获取的都是新实例
  2. 把列表项抽为独立组件,每个组件单独创建Bloc实例:
// 独立列表项组件
class QuestionItem extends StatefulWidget {
  final Question question;
  const QuestionItem({super.key, required this.question});

  @override
  State<QuestionItem> createState() => _QuestionItemState();
}

class _QuestionItemState extends State<QuestionItem> {
  @override
  void initState() {
    super.initState();
    // 仅初始化时触发一次请求
    context.read<UsersWatcherBloc>().add(
          UsersWatcherEvent.watchAllUsers(
            widget.question.userId.getorCrash(),
          ),
        );
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<UsersWatcherBloc, UsersWatcherState>(
      builder: (context, state) {
        // 直接用当前Bloc的状态渲染即可,和其他列表项互不干扰
        return Container(
          color: Colors.white,
          // 原有布局代码
        );
      },
    );
  }
}

// ListView的itemBuilder改造
itemBuilder: (context, index) {
  final question = state.questions.get(index);
  return BlocProvider(
    // 每个列表项创建独立的Bloc实例
    create: (context) => getIt<UsersWatcherBloc>(),
    child: QuestionItem(question: question),
  );
}

内容的提问来源于stack exchange,提问作者Amit Singh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 22:54:03