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
相关产品推荐
相关产品推荐

