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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 07:48:00