如何解决Flutter中对象转可编码对象失败的未处理异常
问题:POST请求序列化失败(Instance of 'TextEditingController')
我需要通过POST方法提交表单数据,设置了学期选填逻辑:选择KHS时显示学期下拉框,未选择则请求体传入空字符串。但点击提交按钮时触发错误:Unhandled Exception: Converting object to an encodable object failed: Instance of 'TextEditingController'。尝试修改后仍有其他错误,以下是我的Widget代码:
class _PengajuanPageState extends State<PengajuanPage> { String? selectedValue; int? selectedSmt; List<String> listItem = [ 'Transkrip Sementara', 'KHS', ]; List<int> khsListItem = [1, 2, 3, 4, 5, 6, 7, 8]; final _formKey = GlobalKey<FormState>(); final _messageController = TextEditingController(); void submitForm() async { String url = Constant.baseURL; String token = await UtilSharedPreferences.getToken(); final response = await http.post( Uri.parse( '$url/auth/mhs_siakad/pengajuan/pengajuan_cetak_transkrip/store', ), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: jsonEncode( { 'jenis_transkrip': selectedValue, 'semester': selectedSmt ?? '', 'message': _messageController, }, ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: primaryColor, title: Center( child: Text( 'Pengajuan Transkrip/KHS', style: bold5, ), ), actions: [ IconButton( icon: const Icon(Icons.history), onPressed: () { Navigator.pushNamed( context, AppRoute.historypengajuanPage, ); }, ) ], ), body: Padding( padding: const EdgeInsets.all(16), child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox( height: 10, ), Text( 'Pilih Jenis Transkrip', style: regular5, ), const SizedBox( height: 8, ), Container( width: double.infinity, height: 50, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), spreadRadius: 1, blurRadius: 9, offset: const Offset( 1, 2, ), ), ], ), child: Padding( padding: const EdgeInsets.all(8.0), child: DropdownButtonHideUnderline( child: DropdownButton<String>( hint: const Text('Pilih Jenis Transkrip'), value: selectedValue, onChanged: (newValue) { setState(() { selectedValue = newValue!; selectedSmt = null; }); }, items: listItem.map((valueItem) { return DropdownMenuItem<String>( value: valueItem, child: Text( valueItem, ), ); }).toList(), ), ), ), ), const SizedBox( height: 10, ), Visibility( visible: selectedValue == 'KHS', child: Container( width: double.infinity, height: 50, decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), spreadRadius: 1, blurRadius: 9, offset: const Offset( 1, 2, ), ), ], ), child: Padding( padding: const EdgeInsets.all(8.0), child: DropdownButtonHideUnderline( child: DropdownButton<int>( hint: const Text('Pilih Semester'), value: selectedSmt, onChanged: (newValue) { setState(() { selectedSmt = newValue; }); }, items: khsListItem.map((value) { return DropdownMenuItem<int>( value: value, child: Text( value.toString(), ), ); }).toList(), ), ), ), ), ), RichText( text: TextSpan( text: 'Keperluan Cetak ', style: GoogleFonts.poppins( textStyle: const TextStyle( color: Colors.black, fontSize: 14, ), ), children: <TextSpan>[ TextSpan( text: '*', style: GoogleFonts.poppins( textStyle: const TextStyle( color: Colors.red, fontSize: 16, ), ), ), ], ), ), const SizedBox( height: 8, ), TextFormField( controller: _messageController, maxLines: 4, decoration: InputDecoration( enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide( color: Colors.white, width: 2.0, ), ), fillColor: Colors.white, focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: const BorderSide( color: Colors.blue, ), ), filled: true, hintText: 'Deskripsikan Keperluan', border: const OutlineInputBorder( borderSide: BorderSide( color: Colors.white, ), ), ), validator: (value) { if (value == null || value.isEmpty) { return 'Keperluan cetak wajib di isi!'; } return null; }, ), const SizedBox( height: 16, ), Text( '*Silahkan isi form dan simpan untuk mengajukan cetak transkrip', style: regular7.copyWith( color: Colors.red, fontStyle: FontStyle.italic, ), ), const SizedBox( height: 16, ), Expanded( child: Align( alignment: Alignment.bottomCenter, child: Container( height: 45, width: double.infinity, decoration: BoxDecoration( color: Colors.green[100], borderRadius: BorderRadius.circular(8), ), child: TextButton( style: TextButton.styleFrom( foregroundColor: Colors.green, ), onPressed: () { if (_formKey.currentState!.validate()) { submitForm(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Processing Data')), ); } }, child: Text( 'Simpan', style: bold5, ), ), ), ), ), ], ), ), ), ); } }
错误原因与修复方案
1. TextEditingController直接传入导致序列化失败
你在jsonEncode中直接传入了_messageController对象,而非它的文本内容。JSON无法序列化Flutter控制器对象,需要改为获取控制器的文本值:_messageController.text。
2. 学期字段类型不统一问题
selectedSmt是int?类型,空值时你用?? ''将其转为字符串,会导致该字段类型时而为int、时而为String,后端大概率无法正常解析。建议统一类型:
- 若后端接受字符串格式的学期,将
selectedSmt转为字符串后赋值,空值传空字符串:selectedSmt?.toString() ?? '' - 若后端接受数字格式,空值可传
null(需确认后端是否允许),或按后端要求处理。
修复后的submitForm方法
void submitForm() async { String url = Constant.baseURL; String token = await UtilSharedPreferences.getToken(); final response = await http.post( Uri.parse( '$url/auth/mhs_siakad/pengajuan/pengajuan_cetak_transkrip/store', ), headers: { 'Authorization': 'Bearer $token', 'Content-Type': 'application/json', }, body: jsonEncode( { 'jenis_transkrip': selectedValue, 'semester': selectedSmt?.toString() ?? '', // 统一转为字符串,空值传空串 'message': _messageController.text, // 获取输入框文本内容 }, ), ); // 新增响应状态处理 if (response.statusCode == 200) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('提交成功')), ); } else { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('提交失败,请重试')), ); } }
额外优化建议
- 在Widget销毁时释放TextEditingController,避免内存泄漏:
@override void dispose() { _messageController.dispose(); super.dispose(); }
- 添加加载状态变量(如
bool isLoading = false),请求期间禁用提交按钮,防止重复点击。
内容的提问来源于stack exchange,提问作者Meikisihka
相关产品推荐
相关产品推荐

