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

Flutter问答应用如何将多个自定义Questions Widget的分数汇总到主Widget

问题原因
  • 错误1:你在子组件中调用Quiz().score += widget.score时,是新创建了一个Quiz实例,并不是当前正在运行的页面实例,所以分数永远只会更新到新实例的临时变量里,不会同步到实际页面的总分。
  • 错误2:Flutter中父子组件传值不能直接操作对方的属性,正确的做法是父组件将回调函数传递给子组件,子组件触发回调将数据回传给父组件。
  • 错误3:你当前的代码只有第一题答对时调用了disableButton(),其他题目答对后没有锁题,用户可以重复点击同一道题的正确答案重复刷分。
  • 错误4:score和questionsAnswered属于可变状态,不应该直接定义在不可变的StatefulWidget类中,应该放在对应的State类中维护。
修复方案

步骤1:改造Questions子组件,添加回调参数

class Questions extends StatefulWidget {
  final String imagePath;
  final String question;
  final String answer1;
  final String answer2;
  final String answer3;
  final String answer4;
  final bool iscorrectAnswer1;
  final bool iscorrectAnswer2;
  final bool iscorrectAnswer3;
  final bool iscorrectAnswer4;
  // 新增回调参数:答题完成后将得分回传给父组件
  final Function(int addScore) onAnswerSubmitted;

  const Questions(
    this.imagePath,
    this.question,
    this.answer1,
    this.answer2,
    this.answer3,
    this.answer4,
    this.iscorrectAnswer1,
    this.iscorrectAnswer2,
    this.iscorrectAnswer3,
    this.iscorrectAnswer4,
    this.onAnswerSubmitted, {
    super.key,
  });

  @override
  _QuestionsState createState() => _QuestionsState();
}

class _QuestionsState extends State<Questions> {
  // 状态移到State类中
  int _currentScore = 0;
  bool _questionsAnswered = false;

  void _disableButton() {
    setState(() {
      _questionsAnswered = true;
    });
    // 触发回调将当前题的得分传给父组件
    widget.onAnswerSubmitted(_currentScore);
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        SizedBox(
          width: 600,
          height: 600,
          child: Image.asset(widget.imagePath),
        ),
        Align(
            alignment: Alignment.topCenter,
            child: Padding(
              padding: const EdgeInsets.only(
                top: 20,
              ),
              child: Text(
                widget.question,
                style: const TextStyle(
                  color: Colors.white,
                  fontSize: 38,
                ),
              ),
            )),
        Padding(
            padding: const EdgeInsets.only(
              top: 40,
            ),
            child: SizedBox(
              width: 500,
              height: 60,
              child: ClipRRect(
                borderRadius: const BorderRadius.all(Radius.circular(15)),
                child: ElevatedButton(
                    style: ButtonStyle(
                      backgroundColor: MaterialStateProperty.all(
                        const Color(0xFF304e60),
                      ),
                    ),
                    child: Text(
                      widget.answer1,
                      style: const TextStyle(
                        color: Colors.white,
                        fontSize: 15,
                      ),
                    ),
                    onPressed: _questionsAnswered == false
                        ? () {
                            setState(() {
                              if (widget.iscorrectAnswer1 == true) {
                                ScaffoldMessenger.of(context).showSnackBar(
                                  const SnackBar(
                                    content: Text('Correct!'),
                                  ),
                                );
                                _currentScore += 1;
                              } else {
                                ScaffoldMessenger.of(context)
                                    .showSnackBar(const SnackBar(
                                  content: Text('Wrong Answer!'),
                                ));
                              }
                              // 不管对错都锁题
                              _disableButton();
                            });
                            print(widget.iscorrectAnswer1);
                            print(_currentScore);
                          }
                        : null),
              ),
            )),
        Padding(
            padding: const EdgeInsets.only(
              top: 10,
            ),
            child: SizedBox(
              width: 500,
              height: 60,
              child: ClipRRect(
                borderRadius: const BorderRadius.all(Radius.circular(15)),
                child: ElevatedButton(
                    style: ButtonStyle(
                        backgroundColor:
                            MaterialStateProperty.all(const Color(0xFF565462))),
                    child: Text(
                      widget.answer2,
                      style: const TextStyle(
                        color: Colors.white,
                        fontSize: 15,
                      ),
                    ),
                    onPressed: _questionsAnswered == false
                        ? () {
                            setState(() {
                              if (widget.iscorrectAnswer2 == true) {
                                ScaffoldMessenger.of(context).showSnackBar(
                                  const SnackBar(
                                    content: Text('Correct!'),
                                  ),
                                );
                                _currentScore += 1;
                              } else {
                                ScaffoldMessenger.of(context)
                                    .showSnackBar(const SnackBar(
                                  content: Text('Wrong Answer!'),
                                ));
                              }
                              _disableButton();
                            });
                          }
                        : null),
              ),
            )),
        Padding(
          padding: const EdgeInsets.only(
            top: 10,
          ),
          child: SizedBox(
            width: 500,
            height: 60,
            child: ClipRRect(
              borderRadius: const BorderRadius.all(Radius.circular(15)),
              child: ElevatedButton(
                  style: ButtonStyle(
                      backgroundColor:
                          MaterialStateProperty.all(const Color(0xFF84693b))),
                  child: Text(
                    widget.answer3,
                    style: const TextStyle(
                      color: Colors.white,
                      fontSize: 15,
                    ),
                  ),
                  onPressed: _questionsAnswered == false
                      ? () {
                          setState(() {
                            if (widget.iscorrectAnswer3 == true) {
                              ScaffoldMessenger.of(context).showSnackBar(
                                const SnackBar(
                                  content: Text('Correct!'),
                                ),
                              );
                              _currentScore += 1;
                            } else {
                              ScaffoldMessenger.of(context)
                                  .showSnackBar(const SnackBar(
                                content: Text('Wrong Answer!'),
                              ));
                            }
                            _disableButton();
                          });
                        }
                      : null),
            ),
          ),
        )
      ],
    );
  }
}

步骤2:改造Quiz父组件,维护总分状态

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

  @override
  _QuizState createState() => _QuizState();
}

class _QuizState extends State<Quiz> {
  // 总分移到State类中维护
  int _totalScore = 0;

  // 子组件回调触发的加分方法
  void _addScore(int scoreToAdd) {
    setState(() {
      _totalScore += scoreToAdd;
    });
    // 可以在这里打印总分验证
    print("当前总分:$_totalScore");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          // 标题栏显示总分方便测试
          title: Text('CyberQuiz  当前总分:$_totalScore'),
        ),
        body: SingleChildScrollView(
            scrollDirection: Axis.vertical,
            child: Column(
              children: [
                Questions(
                  'images/malware_quiz.jpeg',
                  '1. What is a malware?',
                  'Designed to damage computers, servers or any other devices',
                  "Used to get user's credentials",
                  "It's used to destroy networks",
                  '',
                  true,
                  false,
                  false,
                  false,
                  // 把加分方法传给子组件
                  _addScore,
                ),
                // 可以直接添加多个题目实例,总分会自动累加
                Questions(
                  'images/xxx.jpeg',
                  '2. 第二道测试题',
                  '正确选项',
                  '错误选项1',
                  '错误选项2',
                  '',
                  true,
                  false,
                  false,
                  false,
                  _addScore,
                ),
              ],
            )));
  }
}
效果说明

修改后每道题作答完成后,会自动将当前题的得分通过回调传给父组件,父组件更新总分状态后自动刷新UI,多个题目作答后总分会正常累加。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 01:06:02