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

Flutter发送Post请求写入Google Sheet失败问题排查

Flutter写入Google Sheet功能故障排查与修复

问题描述

开发了一个包含TextField和按钮的Flutter应用,预期输入内容后点击按钮将数据写入Google Sheet,但功能无法正常运行。

错误信息

JSON编码错误

Error: Converting object to an encodable object failed: Instance of 'HashSet' at Object.throw [as throw] (localhost:58716/dart_sdk.js:5080:11) at convert._JsonStringStringifier.new.writeObject –

XMLHttpRequest错误

Error: XMLHttpRequest error.
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 963:28 get current
packages/http/src/browser_client.dart 69:22
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/zone.dart 1685:54 runUnary
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 147:18 handleValue
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 766:44 handleValueCallback
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 795:13 _propagateToListeners
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/future_impl.dart 557:7 [_complete]
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/stream_pipe.dart 61:11 _cancelAndValue
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/stream.dart 1530:7
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 334:14 _checkAndCall
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/operations.dart 339:39 dcall
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/html/dart2js/html_dart2js.dart 37309:58
at Object.createErrorWithStack (http://localhost:65241/dart_sdk.js:5093:12)
at Error._throw (http://localhost:65241/dart_sdk.js:20399:18)
at Error.throwWithStackTrace (http://localhost:65241/dart_sdk.js:20396:18)
at async._AsyncCallbackEntry.new.callback (http://localhost:65241/dart_sdk.js:40921:18)
at Object._microtaskLoop (http://localhost:65241/dart_sdk.js:40778:13)
at _startMicrotaskLoop (http://localhost:65241/dart_sdk.js:40784:13)
at http://localhost:65241/dart_sdk.js:36261:9

相关代码

Flutter代码

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;

import 'model.dart';

String tax = 'ert';
final wordi = TextEditingController();
String taxi = wordi.text;

class BillSplit extends StatefulWidget {
  // const BillSplit({Key? key}) : super(key: key);

  @override
  State<BillSplit> createState() => _BillSplitState();
}

class _BillSplitState extends State<BillSplit> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        margin: EdgeInsets.only(left: 20, right: 20),
        child: Column(children: [
          Container(
            alignment: Alignment.centerLeft,
            margin: EdgeInsets.only(top: 30),
            child: Text(
              'split',
              style:
                  GoogleFonts.abel(fontSize: 20, fontWeight: FontWeight.w600),
            ),
          ),
          SizedBox(height: 15),
          Container(
            width: MediaQuery.of(context).size.width,
            height: 300,
            decoration: BoxDecoration(color: Colors.amber),
            child: Column(
              children: [
                Text(
                  'total',
                  style: GoogleFonts.abel(
                      fontSize: 20, fontWeight: FontWeight.w600),
                ),
                Text(
                  '${tax.toString()}',
                  style: GoogleFonts.abel(
                      fontSize: 20, fontWeight: FontWeight.w600),
                ),
                Container(
                  width: MediaQuery.of(context).size.width / 2,
                  child: TextField(
                    keyboardType: TextInputType.number,
                    controller: wordi,
                    onChanged: (value) {
                      setState(() {
                        tax = value;
                      });
                    },
                    decoration: InputDecoration(
                      border: OutlineInputBorder(
                        borderRadius: BorderRadius.circular(20),
                      ),
                      labelText: "Tax",
                      labelStyle: GoogleFonts.abel(
                          fontSize: 20, fontWeight: FontWeight.w300),
                    ),
                  ),
                ),
                RaisedButton(
                  onPressed: () async {
                    // final body = json.encode(tax);
                    final url = Uri.parse(
                        'https://script.google.com/macros/s/AKfycbyt3ECPqAweBMQFgi96BqTkTzE_HPpFr0hwpGf9pisQ1asTp-g2C5geYfy2XAZUvd2-/exec');
                    final response = await http.post(
                      url,
                      // headers: {'Content-Type': 'application/json'},
                      body: json.encode({
                        taxi,
                      }),
                    );

                    print('Response status: ${response.statusCode}');
                    print('Response body: ${response.body}');
                  }, // },
                  child: Text('Open'),
                ),
              ],
            ),
          )
        ]),
      ),
    );
  }
}

// void sendData() {
//   String taxi = wordi.text;
// }

Google Apps Script代码

function doPost(e) {
  var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1z6y1I3b8xm9XLg-W-VhMOn4d8uHSa0Kv6mK_kwVZu7M/edit#gid=0");
var sh = ss.getSheetByName("Sheet1");

  sheetApp(e);
  Logger.log(e.parameter);
}


function sheetApp(e) {
  SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1').getRange(4,1).setValue(e);
}

修复方案

1. 修复JSON编码问题

原代码中{taxi}会创建HashSet而非标准键值对对象,导致JSON编码失败。同时全局变量taxi只会初始化一次,无法获取最新输入值,需修改按钮点击逻辑:

onPressed: () async {
  final url = Uri.parse('https://script.google.com/macros/s/AKfycbyt3ECPqAweBMQFgi96BqTkTzE_HPpFr0hwpGf9pisQ1asTp-g2C5geYfy2XAZUvd2-/exec');
  // 获取最新输入值,用键值对封装
  final body = json.encode({
    'tax': wordi.text,
  });
  final response = await http.post(
    url,
    headers: {'Content-Type': 'application/json'},
    body: body,
  );

  print('Response status: ${response.statusCode}');
  print('Response body: ${response.body}');
},

2. 修正Google Apps Script逻辑

原脚本直接写入整个e对象,无法解析有效数据,需解析JSON请求体并正确写入单元格:

function doPost(e) {
  var ss = SpreadsheetApp.openByUrl("https://docs.google.com/spreadsheets/d/1z6y1I3b8xm9XLg-W-VhMOn4d8uHSa0Kv6mK_kwVZu7M/edit#gid=0");
  var sh = ss.getSheetByName("Sheet1");
  
  // 解析JSON请求体
  var data = JSON.parse(e.postData.contents);
  sheetApp(sh, data.tax);
  Logger.log(data);
  // 返回成功响应,避免前端请求异常
  return ContentService.createTextOutput(JSON.stringify({'status': 'success'})).setMimeType(ContentService.MimeType.JSON);
}

function sheetApp(sheet, taxValue) {
  // 找到下一个空行写入,而非固定第4行
  var nextRow = sheet.getLastRow() + 1;
  sheet.getRange(nextRow, 1).setValue(taxValue);
}

3. 解决XMLHttpRequest错误

确保Google Apps Script已部署为Web应用,且部署设置:

  • 执行权限选择「任何人,甚至匿名」
  • 部署类型选择「Web应用」,更新版本后重新获取URL替换到Flutter代码中

内容的提问来源于stack exchange,提问作者Tomer Kravitz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 04:36:22