Flutter中如何在表格内构建表单提交数值数据至API
Flutter实现每行独立提交的表单API调用
现有代码
表格布局代码
Table( children: [ const TableRow(children: [ Text( '', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( 'Size', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( 'Order', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( 'New Size', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( 'New Order', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( 'Submit', style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ) ]), // Iterate over the breakdowns list and display the sequence information for (var breakdown in snapshot.data![index].breakdowns) TableRow(children: [ Text( '${breakdown.order}', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( '${breakdown.order}', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), Text( '${breakdown.size}', style: const TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: Colors.black, ), ), const TextField( keyboardType: TextInputType.number, decoration: InputDecoration(), ), const TextField( keyboardType: TextInputType.number, decoration: InputDecoration(), ), IconButton( icon: Icon(Icons.check_circle_outline), onPressed: () { // Code to execute when button is pressed }, ) ]) ], ),
API请求函数
Future<http.Response> addLog(int id, int logOrder, int logSize) async { var url = Uri.parse(Config.apiURL + Config.userAddlogAPI.replaceFirst("{id}", id.toString())); final response = await http.post(url, headers: { HttpHeaders.authorizationHeader: 'Token xxxxxxxxxxxxx', }, body: { 'log_Order': logOrder, 'log_Size': logSize, }); if (response.statusCode == 200) { // request was successful, parse the response return response; } else { // request failed, check the status code and response body for more information throw Exception("Failed to add log"); } }
解决方案
要实现每行独立获取输入并提交,核心是为每行的TextField绑定独立的控制器,步骤如下:
- 定义控制器集合
在State类中声明两个Map来存储每行的输入控制器,用breakdown的唯一标识(比如id)作为key:
Map<int, TextEditingController> _newSizeControllers = {}; Map<int, TextEditingController> _newOrderControllers = {};
- 初始化控制器
在获取到breakdowns数据后(比如在FutureBuilder的snapshot处理逻辑中),为每个breakdown创建对应的控制器:
// 假设breakdown有id属性作为唯一标识 for (var breakdown in snapshot.data![index].breakdowns) { _newSizeControllers[breakdown.id] = TextEditingController(); _newOrderControllers[breakdown.id] = TextEditingController(); }
- 绑定控制器到TextField
修改循环中的TextField,去掉const并绑定对应的控制器:
TextField( controller: _newSizeControllers[breakdown.id], keyboardType: TextInputType.number, decoration: const InputDecoration(), ), TextField( controller: _newOrderControllers[breakdown.id], keyboardType: TextInputType.number, decoration: const InputDecoration(), ),
- 实现提交逻辑
在IconButton的onPressed中,获取输入值、转换类型并调用API,同时处理异常和输入验证:
IconButton( icon: const Icon(Icons.check_circle_outline), onPressed: () async { // 获取输入值 final newSizeText = _newSizeControllers[breakdown.id]?.text.trim() ?? ''; final newOrderText = _newOrderControllers[breakdown.id]?.text.trim() ?? ''; // 输入验证 if (newSizeText.isEmpty || newOrderText.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请填写完整数值')), ); return; } // 转换为int类型 final int? newSize = int.tryParse(newSizeText); final int? newOrder = int.tryParse(newOrderText); if (newSize == null || newOrder == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('请输入有效的数字')), ); return; } // 调用API,替换yourTargetId为实际需要传入的id值 try { await addLog(yourTargetId, newOrder, newSize); // 提交成功后的处理 _newSizeControllers[breakdown.id]?.clear(); _newOrderControllers[breakdown.id]?.clear(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('提交成功')), ); } catch (e) { // 处理提交失败 ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('提交失败:${e.toString()}')), ); } }, ),
- 销毁控制器避免内存泄漏
在State类的dispose方法中销毁所有控制器:
@override void dispose() { _newSizeControllers.values.forEach((controller) => controller.dispose()); _newOrderControllers.values.forEach((controller) => controller.dispose()); super.dispose(); }
注意:代码中的breakdown.id需要替换为你实际的breakdown唯一标识字段,yourTargetId需要替换为API所需的id参数值。
内容的提问来源于stack exchange,提问作者A_K
相关产品推荐
相关产品推荐

