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

Flutter Cubit处理400错误未进入指定错误状态反而抛出异常

问题:服务器返回400状态码却未进入OrderFinishError状态,反而触发StateError异常

页面加载时向服务器发送请求,服务器返回400错误状态,但未进入OrderFinishError状态分支,反而执行了throw StateError('err');。类似逻辑在其他页面可正常运行,这是项目收尾的最后任务。

状态定义

abstract class OrderFinishState {}

class OrderFinishNotLoading extends OrderFinishState {}

class OrderFinishLoading extends OrderFinishState {}

class OrderFinishLoaded extends OrderFinishState {
  OrderFinishLoaded({required this.orderFinishModels});
  final OrderFinishModels orderFinishModels;
}

class OrderFinishError extends OrderFinishState {
  OrderFinishError({required this.errorModels});
  final ErrorModels errorModels;
}

Cubit代码

class OrderFinishCubit extends Cubit<OrderFinishState> {
  OrderFinishCubit() : super(OrderFinishNotLoading());

  Future<void> orderFinish() async {
    emit(OrderFinishLoading());

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

    final orderId = prefs.getInt('orderId');

    final response = await http.post(Uri.parse(ConfigUrl.orderFinish),
        headers: {
          "Content-Type": "application/vnd.api+json",
          "Accept": "application/vnd.api+json",
          "Authorization": "Bearer $token",
        },
        body: jsonEncode(<String, dynamic>{
          "order_id": orderId,
        }));

    if (response.statusCode == 200) {
      log('test order finish cubit');
      log(orderId.toString());
      final responseJson = json.decode(response.body) as Map<String, dynamic>;
      log(json.decode(response.body).toString());
      emit(OrderFinishLoaded(
          orderFinishModels: OrderFinishModels.fromJson(responseJson)));
    } else {
      final jsonError = json.decode(response.body) as Map<String, dynamic>;
      log(response.statusCode.toString());
      log(json.decode(response.body).toString());
      emit(OrderFinishError(errorModels: ErrorModels.fromJson(jsonError)));
    }
  }
}

BlocBuilder代码

return BlocProvider<OrderFinishCubit>(
        create: (context) => OrderFinishCubit()..orderFinish(),
        child: Scaffold(
          body: BlocBuilder<OrderFinishCubit, OrderFinishState>(
            builder: (context, state) {
              if (state is OrderFinishNotLoading) {
                return Center(
                  child:
                      Text('Дождитесь загрузки или перезапустите приложение'),
                );
              } else if (state is OrderFinishLoading) {
                return const Center(
                  child: CircularProgressIndicator(),
                );
              } else if (state is OrderFinishLoaded) {
                return Home();
              } else if (state is OrderFinishError) {
                Scaffold(
                    body: SafeArea(
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Text(state.errorModels.message),
                      SizedBox(
                        height: 15,
                      ),
                      InkWell(
                          onTap: () async {
                            context.go('/activeTravel');
                          },
                          child: Container(
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(32),
                                color: ConfigColor.green,
                              ),
                              width: width,
                              height: 44,
                              child: Center(
                                child: Text('На главную',
                                    textAlign: TextAlign.center,
                                    style: TextStyle(
                                        color: Colors.white,
                                        fontSize: 16,
                                        fontWeight: FontWeight.w500)),
                              ))),
                    ],
                  ),
                ));
              }
              throw StateError('err');
            },
          ),
        ));

问题原因与解决方案

1. 直接原因:OrderFinishError分支缺少return

BlocBuilder的OrderFinishError分支中,创建了Scaffold但没有用return返回,导致代码执行完该分支后继续走到最后的throw StateError('err');语句。

修复代码:

else if (state is OrderFinishError) {
  // 添加return返回错误页面
  return Scaffold(
      body: SafeArea(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      crossAxisAlignment: CrossAxisAlignment.center,
      children: [
        Text(state.errorModels.message),
        SizedBox(
          height: 15,
        ),
        InkWell(
            onTap: () async {
              context.go('/activeTravel');
            },
            child: Container(
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(32),
                  color: ConfigColor.green,
                ),
                width: width,
                height: 44,
                child: Center(
                  child: Text('На главную',
                      textAlign: TextAlign.center,
                      style: TextStyle(
                          color: Colors.white,
                          fontSize: 16,
                          fontWeight: FontWeight.w500)),
                ))),
      ],
    ),
  ));
}

2. 潜在问题:未处理请求过程中的异常

Cubit的orderFinish方法没有全局异常捕获,以下场景会导致无法发射OrderFinishError状态:

  • token或orderId为null,导致请求失败
  • 服务器返回的错误响应体无法解析为JSON
  • 网络请求抛出异常(如无网络、超时)

修复代码:

Future<void> orderFinish() async {
  emit(OrderFinishLoading());

  try {
    final prefs = await SharedPreferences.getInstance();
    final token = prefs.getString('token');
    final orderId = prefs.getInt('orderId');

    // 校验必要参数
    if (token == null || orderId == null) {
      emit(OrderFinishError(errorModels: ErrorModels(message: '缺少必要请求参数')));
      return;
    }

    final response = await http.post(Uri.parse(ConfigUrl.orderFinish),
        headers: {
          "Content-Type": "application/vnd.api+json",
          "Accept": "application/vnd.api+json",
          "Authorization": "Bearer $token",
        },
        body: jsonEncode(<String, dynamic>{
          "order_id": orderId,
        }));

    if (response.statusCode == 200) {
      log('test order finish cubit');
      log(orderId.toString());
      final responseJson = json.decode(response.body) as Map<String, dynamic>;
      log(json.decode(response.body).toString());
      emit(OrderFinishLoaded(
          orderFinishModels: OrderFinishModels.fromJson(responseJson)));
    } else {
      Map<String, dynamic> jsonError;
      try {
        jsonError = json.decode(response.body) as Map<String, dynamic>;
      } catch (e) {
        // 处理JSON解析失败的情况
        jsonError = {'message': '服务器返回无效错误信息'};
      }
      log(response.statusCode.toString());
      log(jsonError.toString());
      emit(OrderFinishError(errorModels: ErrorModels.fromJson(jsonError)));
    }
  } catch (e) {
    // 捕获所有请求异常
    log('请求异常: $e');
    emit(OrderFinishError(errorModels: ErrorModels(message: '请求失败,请检查网络')));
  }
}

3. 优化建议:替换throw为默认错误UI

避免因未覆盖的状态导致崩溃,将最后的throw StateError('err');替换为默认错误页面:

// 替换throw语句
return Center(
  child: Text('未知错误,请重试'),
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 17:45:00