php后端必须设置content-type为application/json并处理cors,flutter需显式jsondecode且校验键名;避免bom、隐藏输出及未等待异步。

PHP 后端必须设置正确的 Content-Type
Flutter 的 http.get() 或 http.post() 默认会严格校验响应头,如果 PHP 没显式声明 Content-Type: application/json; charset=utf-8,即使返回的是合法 JSON,Dart 侧也可能解析失败或抛出 FormatException。
常见错误现象:Flutter 控制台报 type 'String' is not a subtype of type 'map<string dynamic>'</string>,说明 Dart 没自动解析 JSON,而是把原始字符串当成了 String。
- 必须在输出 JSON 前调用
header('Content-Type: application/json; charset=utf-8'); - 避免在
header()前有任何输出(包括空格、BOM、echo、print),否则会触发 “Headers already sent” 错误 - 推荐用
json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),防止中文转义和 URL 斜杠被编码
PHP 返回数组要确保键名是字符串且无特殊字符
Flutter 的 jsonDecode() 能解析标准 JSON,但 PHP 数组若含非法键名(如数字开头、空格、点号、中文键),json_encode() 仍能生成 JSON,但 Dart 解析后可能无法用 map['key'] 正常取值(尤其在 Map<string dynamic></string> 类型检查下)。
使用场景:比如你传 ['user_id' => 123, 'full name' => 'Alice'],Dart 里 map['full name'] 可用,但 ide 提示不安全;若用 ['1id' => 123],Dart 会拒绝类型推导为 Map<string dynamic></string>。
立即学习“PHP免费学习笔记(深入)”;
- PHP 中统一用小写字母+下划线命名键名(如
user_id、created_at) - 避免直接
json_encode($_POST)—— 用户提交的键名不可控,应先映射到白名单字段 - 对敏感字段(如密码)务必在
json_encode()前unset()或过滤
Flutter 端要用 jsonDecode() 显式解析,别依赖自动转换
Dart 的 http 包不会自动把响应体转成 Map,它只返回 Response 对象,response.body 永远是 String。所谓“自动解析 JSON”是常见误解。
容易踩的坑:写成 final data = response.body; 就直接当 Map 用,结果运行时报错。
- 必须导入
'dart:convert'并调用jsonDecode(response.body) - 建议包在
try-catch里,捕获FormatException(对应 PHP 返回了非 JSON 内容,比如 PHP 错误堆栈或 HTML) - 若接口返回数组(如
[{"id":1},{"id":2}]),jsonDecode()结果是List<dynamic></dynamic>,需手动.map((e) => MyModel.fromJson(e)).toList()
跨域问题(CORS)在调试时最常卡住请求
Flutter Web 或 android/ios 模拟器访问本地 PHP(如 http://10.0.2.2:8000/api.php)时,浏览器或 webview 会执行 CORS 检查。PHP 不返回正确响应头,请求直接被拦截,Flutter 甚至收不到响应(SocketException 或超时)。
不是所有环境都表现一致:Android 真机直连本机 PHP 通常无 CORS,但 Flutter Web 和 iOS 模拟器一定有。
- PHP 文件开头加这三行:
header('access-Control-Allow-Origin: *');header('Access-Control-Allow-Methods: GET, POST, OPTIONS');header('Access-Control-Allow-Headers: Content-Type'); - 如果用了 POST 且带 Token,还需允许
Authorization头:header('Access-Control-Allow-Headers: Content-Type, Authorization'); - 生产环境禁止用
*,应指定 Flutter 前端域名(如http://localhost:3000)
实际开发中,最难排查的往往是 PHP 输出了隐藏字符(比如 UTF-8 BOM)导致 JSON 解析失败,或者 Flutter 没处理好异步等待就去读数据。这两处没日志、不报明确错误,但整个流程就卡住。