Flutter Dart中通过REST API发送拦截短信及后台监听故障求助
报错原因与修复方案
1. 现有报错修复
你遇到的invalid argument(s) (onerror): the error handler of future.catchError must return a value of the future's type报错,是两个问题共同导致的:
catchError传入的handleError是void返回类型,但interceptedMessage方法返回的是Future<SmsResponse>,catchError要求错误处理函数必须返回和原Future同类型的值,或直接抛出异常。interceptedMessage方法存在多个分支返回值不符合要求:401、其他状态码分支返回的是入参param(dynamic类型),最后的catch块只打印错误没有返回值。
错误处理函数修改
// 修改handleError返回SmsResponse类型,适配catchError要求 SmsResponse handleError(error) { //hideLoading(); if (error is BadRequestException) { var message = error.message; DialogHelper.showErroDialog(description: message); } else if (error is FetchDataException) { var message = error.message; DialogHelper.showErroDialog(description: message); } else if (error is ApiNotRespondingException) { DialogHelper.showErroDialog( description: 'Oops! It took longer to respond.'); } else if (error is SocketException) { print(error); } else { print("All OK"); } // 错误场景返回失败状态的SmsResponse,可根据你定义的SmsResponse结构调整 return SmsResponse(status: "Failed", message: error.toString()); }
API接口类修改
Future<SmsResponse> interceptedMessage(dynamic param) async { var client = http.Client(); String? token = await storage.readSecureToken('key'); if (token == null) { return SmsResponse(status: "Failed", message: "No token stored in storage"); } try { var response = await client .post( Uri.https("baseURL", "endpoint"), headers: <String, String>{ 'Authorization': 'Token $token', 'Content-Type': 'application/json', // 新增请求头,适配json传参 }, body: jsonEncode(param), // 把参数转成json字符串,不能直接传Map ) .timeout(Duration(seconds: TIME_CONST)) .catchError(handleError); if (response.statusCode == 200) { print('Response Body: ${response.body}'); final data = await jsonDecode(response.body); return SmsResponse.fromJson(data); } else if (response.statusCode == 401) { print("Unauthorized Request"); return SmsResponse(status: "Failed", message: "Unauthorized"); } else { print("Bad Input, status code: ${response.statusCode}"); return SmsResponse(status: "Failed", message: "Request failed with code ${response.statusCode}"); } } catch(e){ print(e); return SmsResponse(status: "Failed", message: e.toString()); } finally { client.close(); // 用完关闭client,避免内存泄漏 } }
2. 收到短信自动触发API调用
直接在onMessage回调里拿到短信内容后调用上传接口即可,同时可传入短信的真实发送方、接收时间等信息:
onMessage( SmsMessage message, ) async { setState(() { body = message.body ?? "Error reading message body."; print("$body"); }); // 新增:收到短信后自动调用上传接口 final messaging = ApiService(); messaging.interceptedMessage({ "id": DateTime.now().millisecondsSinceEpoch, // 用时间戳作为唯一id "body": body, "senderName": message.address ?? "未知发送方", // 取真实发送方号码 "timeStamp": DateTime.now().toIso8601String(), // 取真实接收时间 }).then((value) { if (value.status == "Success") { print('Message Intercepted and uploaded'); } else { print('Upload failed: ${value.message}'); } }); }
3. 后台拦截功能实现
telephony插件的后台短信监听要求onBackgroundMessage是顶层函数(不能是类的成员方法),因为后台运行在独立的isolate里,无法访问主线程的类实例和状态,需要单独实现:
- 首先在全局位置定义后台消息处理函数:
// 全局顶层函数,处理后台收到的短信 @pragma('vm:entry-point') // 必须加这个注解,避免release打包时被混淆 void onBackgroundMessage(SmsMessage message) async { // 后台isolate需要重新初始化依赖,比如存储、ApiService await storage.init(); // 按你使用的存储组件初始化逻辑调整 final messaging = ApiService(); // 调用上传接口,逻辑和前台一致 await messaging.interceptedMessage({ "id": DateTime.now().millisecondsSinceEpoch, "body": message.body ?? "", "senderName": message.address ?? "未知发送方", "timeStamp": DateTime.now().toIso8601String(), }); }
- 安卓端需要在
AndroidManifest.xml里添加必要权限:
<uses-permission android:name="android.permission.RECEIVE_SMS" /> <uses-permission android:name="android.permission.READ_SMS" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" />
- 建议在应用启动时就申请短信权限,不要等到页面初始化才申请,避免后台场景下权限失效。
内容的提问来源于stack exchange,提问作者needHelp
相关产品推荐
相关产品推荐

