Flutter使用http库发送put请求时报bool无法转为String的类型转换错误
原问题翻译
这是我的完整代码,我没有编写模型类,因为我不需要接收服务端的返回响应。
import 'dart:convert'; import 'package:attendance_worklog/AttendanceLog/attendanceLogModel.dart'; import 'package:attendance_worklog/AttendanceLog/attendanceLogService.dart'; import 'package:attendance_worklog/login/loginModel.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:percent_indicator/linear_percent_indicator.dart'; import 'package:http/http.dart' as http; class AttendanceLog extends StatefulWidget { LoginModel? data; AttendanceLog({Key? key, required this.data}) : super(key: key); @override _AttendanceLogState createState() => _AttendanceLogState(); } class _AttendanceLogState extends State<AttendanceLog> { List<AttendanceLogModel> _attendanceLogModel = []; List<String> day = ['day', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']; @override void initState() { // TODO: implement initState super.initState(); AttendanceLogService _attendanceLogService = AttendanceLogService(data: widget.data); _attendanceLogService.getLog().then((value) { setState(() { _attendanceLogModel = value; }); }); } @override Widget build(BuildContext context) { int flag = 0; int per() { for (var i = 0; i < _attendanceLogModel.length; i++) { if (_attendanceLogModel[i].attendance == true) { flag = flag + 1; } } return flag; } int p = per(); return Scaffold( appBar: AppBar( backgroundColor: Colors.white, leading: IconButton( icon: Icon(Icons.arrow_back), onPressed: () => Navigator.of(context).pop(), color: Color(0xff979797), iconSize: 30, ), title: Text( 'Attendance Log', style: TextStyle( color: Color(0xffA468AB), fontWeight: FontWeight.w700, fontFamily: 'Merriweather', fontSize: 32), ), actions: [ IconButton( onPressed: (){}, icon: Icon(Icons.check_outlined)) ], ), body: ListView( children: [ Padding( padding: EdgeInsets.symmetric(horizontal: 12, vertical: 25), child: Column( children: [ LinearPercentIndicator( linearStrokeCap: LinearStrokeCap.roundAll, lineHeight: 18.0, percent: p / _attendanceLogModel.length, backgroundColor: Color(0xffFF0000), progressColor: Color(0xff14FF00), ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( '${(p / _attendanceLogModel.length) * 100}', style: TextStyle( fontWeight: FontWeight.w700, fontSize: 25, fontFamily: 'Merriweather'), ), Text( '${100 - ((p / _attendanceLogModel.length) * 100)}', style: TextStyle( fontWeight: FontWeight.w700, fontSize: 25, fontFamily: 'Merriweather'), ) ], ) ], ), ), Container( height: 1, width: double.maxFinite, color: Color(0xffD3D3D3), ), ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, physics: NeverScrollableScrollPhysics(), itemCount: null == _attendanceLogModel ? 0 : _attendanceLogModel.length, itemBuilder: (context, index) { AttendanceLogModel attendanceLogModel = _attendanceLogModel[index]; void toggleSwitch(bool value) { String hom = ''; if (attendanceLogModel.attendance == false) { setState(() { attendanceLogModel.attendance = true; }); bool att = attendanceLogModel.attendance; String id = attendanceLogModel.id; PutAttendance send = new PutAttendance(data: widget.data); var response = send.sendAttendance(att, id, hom); } else { setState(() { attendanceLogModel.attendance = false; }); PutAttendance send = new PutAttendance(data: widget.data); var response = send.sendAttendance(attendanceLogModel.attendance, attendanceLogModel.id, hom); } } return Padding( padding: EdgeInsets.all(10.0), child: ListTile( title: Text( 'Meet: ${attendanceLogModel.name}', style: TextStyle( fontSize: 17, fontFamily: 'Merriweather', fontWeight: FontWeight.w900), ), subtitle: Text( '${day[attendanceLogModel.start.weekday]}, ${attendanceLogModel.start.day}/${attendanceLogModel.start.month}/${attendanceLogModel.start.year}\n${attendanceLogModel.start.hour}:${attendanceLogModel.start.minute} to ${attendanceLogModel.end.hour}:${attendanceLogModel.end.minute}', style: TextStyle( fontSize: 16, fontFamily: 'Merriweather', color: Color(0xff000000), fontWeight: FontWeight.w100), ), trailing: Transform.scale( scale: 0.8, child: CupertinoSwitch( activeColor: Color(0xff14FF00), value: attendanceLogModel.attendance, onChanged: toggleSwitch, trackColor: Color(0xffFF0000), ), ), tileColor: Color(0xffF2F2F2), ), ); }), ], ), ); } } class PutAttendance{ LoginModel? data; PutAttendance({required this.data}); var url = Uri.parse('http://10.0.2.2:5001/naf-vip-server/us-central1/api/attendance'); Future<http.Response> sendAttendance(bool attend, String id, String hom){ return http.put(url, headers: { 'Authorization': 'Bearer ${data!.token}' }, body:{ "attendance": attend, "meetId": id, "hom": hom } ); } }
运行后抛出如下异常:Unhandled Exception: type 'bool' is not a subtype of type 'String' in type cast,同时服务端的数据也没有被更新,我无法定位问题的位置和原因,恳请帮忙解决该问题。
异常栈如下:
E/flutter (12852): [ERROR:flutter/lib/ui/ui_dart_state.cc(199)] Unhandled Exception: type 'bool' is not a subtype of type 'String' in type cast E/flutter (12852): #0 CastMap.forEach.<anonymous closure> (dart:_internal/cast.dart:288:25) E/flutter (12852): #1 _LinkedHashMapMixin.forEach (dart:collection-patch/compact_hash.dart:397:8) E/flutter (12852): #2 CastMap.forEach (dart:_internal/cast.dart:287:13) E/flutter (12852): #3 mapToQuery (package:http/src/utils.dart:17:7) E/flutter (12852): #4 Request.bodyFields= (package:http/src/request.dart:137:12) E/flutter (12852): #5 BaseClient._sendUnstreamed (package:http/src/base_client.dart:87:17) E/flutter (12852): #6 BaseClient.put (package:http/src/base_client.dart:37:7) E/flutter (12852): #7 put.<anonymous closure> (package:http/http.dart:92:16) E/flutter (12852): #8 _withClient (package:http/http.dart:164:20) E/flutter (12852): #9 put (package:http/http.dart:91:5) E/flutter (12852): #10 PutAttendance.sendAttendance (package:attendance_worklog/AttendanceLog/attendanceLog.dart:429:12) E/flutter (12852): #11 _AttendanceLogState.build.<anonymous closure>.toggleSwitch (package:attendance_worklog/AttendanceLog/attendanceLog.dart:136:41) E/flutter (12852): #12 _CupertinoSwitchState._handleTap (package:flutter/src/cupertino/switch.dart:226:24) E/flutter (12852): #13 GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:182:24) E/flutter (12852): #14 TapGestureRecognizer.handleTapUp (package:flutter/src/gestures/tap.dart:607:11) E/flutter (12852): #15 BaseTapGestureRecognizer._checkUp (package:flutter/src/gestures/tap.dart:296:5) E/flutter (12852): #16 BaseTapGestureRecognizer.acceptGesture (package:flutter/src/gestures/tap.dart:267:7) E/flutter (12852): #17 GestureArenaManager.sweep (package:flutter/src/gestures/arena.dart:157:27) E/flutter (12852): #18 GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:443:20) E/flutter (12852): #19 GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:419:22) E/flutter (12852): #20 RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:287:11) E/flutter (12852): #21 GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:374:7) E/flutter (12852): #22 GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:338:5) E/flutter (12852): #23 GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:296:7) E/flutter (12852): #24 GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:279:7) E/flutter (12852): #25 _rootRunUnary (dart:async/zone.dart:1370:13) E/flutter (12852): #26 _CustomZone.runUnary (dart:async/zone.dart:1265:19) E/flutter (12852): #27 _CustomZone.runUnaryGuarded (dart:async/zone.dart:1170:7) E/flutter (12852): #28 _invoke1 (dart:ui/hooks.dart:182:10) E/flutter (12852): #29 PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:282:7) E/flutter (12852): #30 _dispatchPointerDataPacket (dart:ui/hooks.dart:96:31)
问题根因
调用http.put方法时直接传入了包含布尔类型值的Map作为body参数,Dart http库默认将该类Map识别为application/x-www-form-urlencoded格式的表单数据,要求所有值的类型都为String,你传入的attendance字段为bool类型,触发了类型转换异常。
解决方案
根据服务端接口接收的参数格式,选择对应修复方案:
- 方案1:服务端接收表单格式参数
将布尔值转为字符串即可,修改PutAttendance类的sendAttendance方法:
Future<http.Response> sendAttendance(bool attend, String id, String hom){ return http.put(url, headers: { 'Authorization': 'Bearer ${data!.token}' }, body:{ "attendance": attend.toString(), "meetId": id, "hom": hom } ); }
- 方案2:服务端接收JSON格式参数
将参数序列化为JSON字符串,同时指定请求头Content-Type为application/json:
Future<http.Response> sendAttendance(bool attend, String id, String hom){ return http.put(url, headers: { 'Authorization': 'Bearer ${data!.token}', 'Content-Type': 'application/json' }, body: jsonEncode({ "attendance": attend, "meetId": id, "hom": hom }) ); }
额外优化建议
你当前调用sendAttendance方法时没有添加await处理异步结果,也没有做异常校验,建议修改toggleSwitch方法为异步方法,根据接口响应结果处理异常回滚逻辑,避免界面状态和服务端数据不一致:
void toggleSwitch(bool value) async { String hom = ''; final oldAttendanceStatus = attendanceLogModel.attendance; setState(() { attendanceLogModel.attendance = value; }); PutAttendance send = PutAttendance(data: widget.data); try{ var response = await send.sendAttendance(value, attendanceLogModel.id, hom); if(response.statusCode < 200 || response.statusCode >= 300){ // 请求失败回滚状态 setState(() { attendanceLogModel.attendance = oldAttendanceStatus; }); } }catch(e){ // 请求异常回滚状态 setState(() { attendanceLogModel.attendance = oldAttendanceStatus; }); } }
内容的提问来源于stack exchange,提问作者Ayan Khan
相关产品推荐
相关产品推荐

