Flutter点击提交问卷按钮添加Linear Progress Indicator及无survey_kit问卷方案咨询
Flutter调查问卷功能实现方案
一、现有survey_kit代码改造(实现提交后跳转动画进度页需求)
修改步骤:
- 删除原代码中
task.addNavigationRule相关的导航规则,避免拦截提交后的跳转逻辑 - 修改
SurveyKit组件的onResult回调,触发页面跳转 - 新增带动画的进度展示页面,同时包含圆形、线性进度指示器和百分比动画
// 替换原SurveyKit的onResult回调逻辑 onResult: (SurveyResult result) { // 提交问卷后直接跳转到动画进度页 Navigator.of(context).push( MaterialPageRoute(builder: (context) => const AnimatedProgressPage()) ); },
// 新增动画进度页代码 class AnimatedProgressPage extends StatefulWidget { const AnimatedProgressPage({super.key}); @override State<AnimatedProgressPage> createState() => _AnimatedProgressPageState(); } class _AnimatedProgressPageState extends State<AnimatedProgressPage> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _progressAnimation; @override void initState() { super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 3)); _progressAnimation = Tween<double>(begin: 0, end: 1).animate(_controller) ..addListener(() => setState(() {})); // 启动进度动画 _controller.forward(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text("提交中"), backgroundColor: Colors.cyan), body: Padding( padding: const EdgeInsets.all(24.0), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ // 圆形进度指示器 CircularProgressIndicator( value: _progressAnimation.value, color: Colors.cyan, strokeWidth: 8, ), const SizedBox(height: 40), // 线性进度指示器 LinearProgressIndicator( value: _progressAnimation.value, color: Colors.cyan, backgroundColor: Colors.cyan.shade100, minHeight: 12, borderRadius: BorderRadius.circular(6), ), const SizedBox(height: 30), // 动画百分比文本 Text( "${(_progressAnimation.value * 100).toInt()}%", style: const TextStyle(fontSize: 32, fontWeight: FontWeight.bold, color: Colors.cyan), ) ], ), ), ); } }
二、不使用survey_kit包的更优实现方案
方案优势:
- 灵活度更高:完全自定义UI、交互逻辑,不受第三方包的功能限制
- 包体积更小:无冗余依赖,减少安装包大小
- 维护成本更低:代码完全可控,出现问题不需要排查第三方包源码
核心实现思路:
- 自定义问卷数据模型,适配不同题型(单选、多选、填空等)
- 用
IndexedStack/PageView管理问卷步骤,通过状态管理工具统一维护当前步骤、用户答案 - 自主实现表单校验、答案存储、提交逻辑,扩展性更强
核心代码示例:
// 1. 问卷步骤通用模型 class SurveyStep { final String title; final String content; final List<String>? options; SurveyStep({required this.title, required this.content, this.options}); } // 2. 自定义问卷主页面 class CustomSurveyPage extends StatefulWidget { const CustomSurveyPage({super.key}); @override State<CustomSurveyPage> createState() => _CustomSurveyPageState(); } class _CustomSurveyPageState extends State<CustomSurveyPage> { int currentStep = 0; // 问卷步骤配置,可根据需求扩展 final List<SurveyStep> steps = [ SurveyStep(title: "尊敬的用户,您好!", content: "请完成以下简短问卷,点击开始按钮答题", options: ["开始答题"]), SurveyStep(title: "问题1/7", content: "您最常使用的寄件类型是?", options: ["信件", "包裹", "大件寄件", "以上都不是"]), SurveyStep(title: "问题2/7", content: "您是否认可常用寄件类型的定价?", options: ["是", "否", "不确定"]), // 剩余问题按上述格式补充即可 SurveyStep(title: "调研完成", content: "感谢您的参与,点击提交完成问卷", options: ["提交问卷"]), ]; final Map<int, String> userAnswers = {}; @override Widget build(BuildContext context) { final step = steps[currentStep]; return Scaffold( backgroundColor: Colors.white, body: Padding( padding: const EdgeInsets.all(24), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 60), Text(step.title, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold, color: Colors.black87)), const SizedBox(height: 20), Text(step.content, style: const TextStyle(fontSize: 16, color: Colors.black54)), const SizedBox(height: 40), // 选项渲染 ...step.options!.map((option) => GestureDetector( onTap: () { if(currentStep == 0) { setState(() => currentStep++); } else if(currentStep == steps.length - 1) { // 提交问卷跳转进度页 Navigator.of(context).push(MaterialPageRoute(builder: (context) => const AnimatedProgressPage())); } else { userAnswers[currentStep] = option; setState(() => currentStep++); } }, child: Container( width: double.infinity, margin: const EdgeInsets.only(bottom: 12), padding: const EdgeInsets.all(16), decoration: BoxDecoration( border: Border.all(color: Colors.cyan), borderRadius: BorderRadius.circular(8), ), child: Text(option, style: const TextStyle(fontSize: 15)), ), )) ], ), ), ); } }
内容的提问来源于stack exchange,提问作者Steph 12222
相关产品推荐
相关产品推荐

