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

解决Bloc中StateError: Cannot emit new states after calling close错误

解决StateError: Bad state: Cannot emit new states after calling close问题

问题原因

  1. 扫码流重复触发:scannedDataStream会持续输出扫码结果,即使已经成功识别一次,仍会多次触发回调,导致重复调用Cubit的scanner方法
  2. Cubit作用域混乱:在build方法内创建BlocProvider,页面重建时会重复实例化Cubit,旧Cubit被销毁后仍可能执行异步请求并尝试发射状态
  3. 未及时停止监听:扫码成功后未暂停相机和取消流订阅,导致后续无效的状态发射请求

修复方案

1. 修改扫码页面(QRViewExample)

import 'dart:developer';
import 'dart:async';

import 'package:eco_city/features/presentation/scanner/cubit/scanner_cubit.dart';
import 'package:eco_city/features/presentation/scanner/cubit/scanner_state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'dart:io';
import '../home/home.dart';

import 'package:qr_code_scanner/qr_code_scanner.dart';

class QRViewExample extends StatefulWidget {
  @override
  _QRViewExampleState createState() => _QRViewExampleState();
}

class _QRViewExampleState extends State<QRViewExample> {
  final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
  Barcode? result;
  QRViewController? controller;
  ScannerCubit? _scannerCubit;
  StreamSubscription? _scanSubscription;
  bool _isProcessing = false; // 防止重复请求

  @override
  void initState() {
    super.initState();
    _scannerCubit = ScannerCubit(); // 初始化Cubit,避免build重复创建
  }

  @override
  void reassemble() {
    super.reassemble();
    print('первая функция');
    if (Platform.isAndroid) {
      controller?.pauseCamera();
    } else if (Platform.isIOS) {
      controller?.resumeCamera();
    }
  }

  String? getQr(Barcode? qr) {
    if (qr != null) {
      RegExp exp = RegExp(r"[^0-9]+");
      final stringFormat = qr.rawContent.replaceAll(exp, '');
      return stringFormat;
    }
    return null;
  }

  @override
  Widget build(BuildContext context) {
    return BlocProvider.value(
      value: _scannerCubit!,
      child: BlocBuilder<ScannerCubit, ScannerState>(
        builder: (context, state) {
          if (state is ScannerNotScan) {
            return Scaffold(
              body: Column(
                children: <Widget>[
                  Expanded(
                    flex: 5,
                    child: QRView(
                      key: qrKey,
                      onQRViewCreated: _onQRViewCreated,
                    ),
                  ),
                  Expanded(
                    flex: 1,
                    child: Center(
                      child: (result != null)
                          ? Text(
                              'Barcode Type: ${describeEnum(result!.format)}   Data: ${result!.rawContent}')
                          : Text('Scan a code'),
                    ),
                  )
                ],
              ),
            );
          } else if (state is ScannerLoading) {
            return Scaffold(
              body: Center(child: CircularProgressIndicator()),
            );
          } else if (state is ScannerLoaded) {
            return Home();
          } else if (state is ScannerError) {
            return Scaffold(
              body: Center(child: Text('扫码失败,请重试')),
            );
          }
          throw StateError('无效状态');
        },
      ),
    );
  }

  void _onQRViewCreated(QRViewController controller) async {
    print('последняя функция');
    this.controller = controller;
    _scanSubscription = controller.scannedDataStream.listen((scanData) {
      if (_isProcessing) return; // 正在处理时忽略新结果
      setState(() {
        result = scanData;
      });
      _scanSubscription?.cancel(); // 取消订阅,避免重复触发
      controller.pauseCamera(); // 暂停相机
      getNextScreen();
    });
  }

  void getNextScreen() async {
    if (result != null && !_isProcessing) {
      _isProcessing = true;
      final qrCode = getQr(result!);
      if (qrCode != null) {
        await context.read<ScannerCubit>().scanner(qrCode);
      }
      _isProcessing = false;
    }
  }

  @override
  void dispose() {
    _scanSubscription?.cancel(); // 清理订阅
    controller?.dispose();
    _scannerCubit?.close(); // 手动关闭Cubit
    super.dispose();
  }
}

2. 修改ScannerCubit

import 'dart:convert';
import 'dart:developer';

import 'package:eco_city/features/data/scanner/scanner_models.dart';
import 'package:eco_city/features/presentation/scanner/cubit/scanner_state.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;

import '../../config.dart';

class ScannerCubit extends Cubit<ScannerState> {
  ScannerCubit() : super(ScannerNotScan());

  Future<void> scanner(String qr) async {
    // 先检查Cubit是否已关闭,避免后续无效操作
    if (isClosed) return;
    
    emit(ScannerLoading());

    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString('token');

    final response = await http.post(Uri.parse(ConfigUrl.sendQrCodeScanner),
        headers: {
          "Content-Type": "application/vnd.api+json",
          "Accept": "application/vnd.api+json",
          "Authorization": "Bearer $token"
        },
        body: jsonEncode<String, dynamic>({
          "qr_code": qr,
        }));
    
    // 请求完成后再次检查是否关闭
    if (isClosed) return;
    
    if (response.statusCode == 200) {
      log(qr);
      final responseJson = json.decode(response.body) as Map<String, dynamic>;
      emit(ScannerLoaded(scannerModel: ScannerModel.fromJson(responseJson)));
    } else {
      log(qr);
      log(response.statusCode.toString());
      log(json.decode(response.body).toString());
      emit(ScannerError());
    } 
  }
}

关键修复点

  • 将Cubit初始化移到initState,使用BlocProvider.value提供实例,避免页面重建重复创建Cubit
  • 扫码成功后立即取消scannedDataStream订阅并暂停相机,防止重复触发逻辑
  • 添加_isProcessing标记,避免同一时间发起多个请求
  • 在Cubit的scanner方法开头和请求完成后都检查isClosed,确保不会在Cubit关闭后发射状态

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 00:10:15