Flutter如何追踪数量与类型未知的组件的用户答案?
解决方案
1. 全局存储答案状态
在页面的State类中定义一个Map,用问题的唯一ID作为键(避免列表顺序变化导致答案对应错误),存储每个问题的答案:
Map<int, dynamic> _answers = {}; // 假设FeedbackQuestion包含int类型的id字段
2. 改造题型组件,添加回调函数
每个自定义题型组件需要接收onChanged回调,将用户的选择/输入传递回父组件。以BooleanCheckboxQuestion为例:
class BooleanCheckboxQuestion extends StatelessWidget { final ValueChanged<bool> onChanged; final bool? initialValue; // 可选:支持回显初始答案 const BooleanCheckboxQuestion({ Key? key, required this.onChanged, this.initialValue, }) : super(key: key); @override Widget build(BuildContext context) { return Checkbox( value: initialValue ?? false, onChanged: (value) { if (value != null) { onChanged(value); } }, ); } }
其他题型组件按答案类型调整泛型:
- 单选组件:
ValueChanged<String>/ValueChanged<int>(根据选项值类型) - 多选组件:
ValueChanged<List<String>> - 短文本/长文本组件:
ValueChanged<String> - 数字类型组件:
ValueChanged<int>/ValueChanged<double>
3. 渲染时传递回调,实时更新答案
在ListView.builder中,给每个组件传入onChanged回调,触发状态更新:
snapshot.data![index].answer_type! == "bc" ? BooleanCheckboxQuestion( onChanged: (value) { setState(() { _answers[snapshot.data![index].id!] = value; }); }, ) : snapshot.data![index].answer_type! == "bs" ? BooleanSelectionQuestion( onChanged: (value) { setState(() { _answers[snapshot.data![index].id!] = value; }); }, ) // 其余题型组件均按此逻辑添加onChanged回调
4. 实现提交逻辑,发送POST请求
定义提交方法,整理答案格式并发送请求:
Future<void> _submitFeedback() async { // 校验必填项(根据业务需求调整) for (var question in snapshot.data!) { if (question.isRequired && !_answers.containsKey(question.id)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("请完成所有必填问题")), ); return; } } // 整理提交数据(格式需匹配API要求) List<Map<String, dynamic>> submitData = snapshot.data!.map((question) { return { "question_id": question.id, "answer": _answers[question.id] ?? "", // 未回答问题传空或默认值 }; }).toList(); // 发送POST请求 Map<String, String> requestHeaders = { 'Content-type': 'application/json', 'Accept': 'application/json', 'Authorization': 'Token $apiToken' }; try { final response = await http.post( Uri.parse("$submitFeedbackURL"), headers: requestHeaders, body: json.encode({"answers": submitData}), ); if (response.statusCode == 200 || response.statusCode == 201) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("提交成功")), ); // 提交成功后清空答案或返回上一页 setState(() { _answers.clear(); }); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("提交失败,请重试")), ); } } catch (e) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text("网络异常,请检查连接")), ); } }
5. 添加提交按钮
将原ListView包裹在Column中,底部添加提交按钮:
Column( children: [ Expanded( child: ListView.builder( shrinkWrap: true, itemCount: snapshot.data!.length, itemBuilder: (context, index) { // 已添加回调的渲染逻辑 }, ), ), Padding( padding: EdgeInsets.all(16), child: ElevatedButton( onPressed: _submitFeedback, child: Text("提交问卷"), style: ElevatedButton.styleFrom( minimumSize: Size(double.infinity, 50), ), ), ), ], )
内容的提问来源于stack exchange,提问作者Gse
相关产品推荐
相关产品推荐

