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

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绑定独立的控制器,步骤如下:

  1. 定义控制器集合
    在State类中声明两个Map来存储每行的输入控制器,用breakdown的唯一标识(比如id)作为key:
Map<int, TextEditingController> _newSizeControllers = {};
Map<int, TextEditingController> _newOrderControllers = {};
  1. 初始化控制器
    在获取到breakdowns数据后(比如在FutureBuilder的snapshot处理逻辑中),为每个breakdown创建对应的控制器:
// 假设breakdown有id属性作为唯一标识
for (var breakdown in snapshot.data![index].breakdowns) {
  _newSizeControllers[breakdown.id] = TextEditingController();
  _newOrderControllers[breakdown.id] = TextEditingController();
}
  1. 绑定控制器到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(),
),
  1. 实现提交逻辑
    在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()}')),
      );
    }
  },
),
  1. 销毁控制器避免内存泄漏
    在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:10:42