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

Flutter向Firebase添加数据时出现StateError状态错误求助

解决Flutter Bloc/Cubit报错:Bad state: Cannot emit new states after calling close

核心问题分析

  • Cubit提前关闭:点击添加按钮时,先调用add方法(异步写入Firestore),紧接着执行widget.onSave()。如果onSave是关闭当前页面(如路由pop),会直接销毁BlocProvider并调用Cubit的close()方法;而Firestore写入完成后执行emit时,Cubit已处于关闭状态,触发报错。
  • Firestore API误用:FirebaseFirestore.collection.add()返回的是Future<DocumentReference>,你错误将其强制转换为StreamSubscription?,导致_streamSubscription是无效对象,后续close()时的cancel()操作存在潜在风险。

修复方案

1. 修正Cubit的Firestore调用逻辑

移除错误的StreamSubscription相关代码,因为add是单次写入操作,不需要监听流:

import 'dart:async';
import 'package:bloc/bloc.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
part 'add_page_state.dart';

class AddPageCubit extends Cubit<AddPageState> {
  AddPageCubit() : super(const AddPageState());

  Future<void> add(
    String product,
    String shopName,
    String category,
  ) async {
    try {
      // 直接执行Firestore写入,无需赋值给StreamSubscription
      await FirebaseFirestore.instance.collection('shoppingList').add(
        {
          'product': product,
          'shopName': shopName,
          'category': category,
        },
      );
      emit(const AddPageState(saved: true));
    } catch (error) {
      emit(AddPageState(errorMessage: error.toString()));
    }
  }

  @override
  Future<void> close() {
    return super.close();
  }
}

2. 调整页面按钮执行顺序

不要在调用add后立即执行onSave,而是监听Cubit状态变化,确认写入完成后再触发页面关闭:

BlocBuilder<AddPageCubit, AddPageState>(
  builder: (context, state) {
    // 监听saved状态,在帧绘制完成后执行onSave
    if (state.saved) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        widget.onSave();
      });
    }

    if (state.errorMessage.isNotEmpty) {
      return Center(
        child: Text('Someting went wrong ${state.errorMessage}!'),
      );
    }

    return Center(
      child: Padding(
        padding: const EdgeInsets.all(25.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 原有输入组件保持不变
            // ...
            ElevatedButton(
              onPressed: _product == null || _shopName == null
                  ? null
                  : () {
                      context.read<AddPageCubit>().add(
                            _product!,
                            _shopName!,
                            _category!,
                          );
                      // 移除此处的widget.onSave()
                    },
              child: const Text('Dodaj'),
            ),
          ],
        ),
      ),
    );
  },
)

3. 可选:优化状态类(提升扩展性)

给AddPageState添加copyWith方法,避免每次创建全新状态对象:

part of 'add_page_cubit.dart';

class AddPageState {
  const AddPageState({
    this.saved = false,
    this.errorMessage = '',
  });

  final bool saved;
  final String errorMessage;

  AddPageState copyWith({
    bool? saved,
    String? errorMessage,
  }) {
    return AddPageState(
      saved: saved ?? this.saved,
      errorMessage: errorMessage ?? this.errorMessage,
    );
  }
}

后续emit状态时可简化为:

emit(state.copyWith(saved: true));

验证效果

修改后,点击添加按钮会先完成Firestore写入,emitsaved: true状态,再在页面帧绘制完成后执行onSave关闭页面。此时Cubit尚未被销毁,不会触发emit时的关闭状态错误,同时修正了Firestore API的误用问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 04:00:29