Flutter题库优化需求:解决JSON重复加载与按钮选中变色问题
Flutter题库功能优化方案
一、解决重复加载JSON数据问题
问题原因
当前代码中FutureBuilder的future参数直接传入getQuestionData(),每次组件build(比如切换题目调用setState时)都会重新执行网络请求,导致重复加载数据。
解决方案
在initState生命周期中一次性加载所有题目数据,加载完成后更新questionList并刷新界面,不再依赖FutureBuilder重复发起请求。
修改要点:
- 新增
isLoading变量控制加载状态UI显示; - 在
initState中调用数据加载方法,完成后更新列表并关闭加载状态; - 移除
FutureBuilder,直接根据isLoading渲染对应界面。
二、实现选项按钮选中变色交互
问题原因
当前控制按钮颜色的click是局部变量,每次build都会重置,无法保留选中状态;同时缺少记录当前选中选项的全局变量。
解决方案
新增变量记录当前选中的选项标识(如'1'/'2'/'3'/'4'),点击按钮时更新该变量,根据选项是否被选中动态设置按钮样式,同时处理得分逻辑避免重复加分。
修改要点:
- 新增
selectedOption变量存储当前选中的选项序号; - 在按钮组件中根据
selectedOption判断选中状态,动态调整颜色; - 点击按钮时更新选中状态,同步处理分数增减。
修改后的完整代码
import 'package:flutter/material.dart'; import 'show_question_model.dart'; import 'package:http/http.dart' as http; import 'dart:convert'; class ShowQuestions extends StatefulWidget { const ShowQuestions({super.key}); @override State<ShowQuestions> createState() => _ShowQuestionsState(); } class _ShowQuestionsState extends State<ShowQuestions> { List<Question> questionList = []; int currentQuestionIndex = 0; int score = 0; bool isLoading = true; // 加载状态标识 String? selectedOption; // 当前选中的选项序号 Future getQuestionData() async { var response = await http .get(Uri.http("www.ananmanan.lk", "app/getQuestionList.php")); var jsonData = jsonDecode(response.body); List<Question> questions = []; for (var u in jsonData) { Question question = Question( u['ques'], u['ans1'], u['ans2'], u['ans3'], u['ans4'], u['correct_ans'], ); questions.add(question); } return questions; } @override void initState() { super.initState(); // 初始化时一次性加载所有题目 _loadQuestions(); } Future<void> _loadQuestions() async { final questions = await getQuestionData(); setState(() { questionList = questions; isLoading = false; }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Question Paper'), centerTitle: true, ), body: isLoading ? const Center(child: Text('Loading...')) : Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 32), child: Column( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _questionWidget(), _answerList(), _nextButton(), ], ), ), ); } _questionWidget() { return Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Question ${currentQuestionIndex + 1}/${questionList.length}', style: const TextStyle( color: Colors.white, fontSize: 20.0, fontWeight: FontWeight.w600, ), ), const SizedBox( height: 20.0, ), Container( width: double.infinity, alignment: Alignment.center, padding: const EdgeInsets.all(32.0), decoration: BoxDecoration( color: Colors.orangeAccent, borderRadius: BorderRadius.circular(16)), child: Text( questionList[currentQuestionIndex].ques, style: const TextStyle( color: Colors.white, fontSize: 18.0, fontWeight: FontWeight.w600, ), ), ), ], ); } _answerList() { return Container( child: Column(children: [ _answerButton(questionList[currentQuestionIndex].ans1, '1'), _answerButton(questionList[currentQuestionIndex].ans2, '2'), _answerButton(questionList[currentQuestionIndex].ans3, '3'), _answerButton(questionList[currentQuestionIndex].ans4, '4'), ]), ); } Widget _answerButton(String ansText, String optionId) { bool isSelected = selectedOption == optionId; bool isCorrect = questionList[currentQuestionIndex].correct_ans == optionId; if (ansText != '') { return Container( width: double.infinity, margin: const EdgeInsets.symmetric(vertical: 2), height: 48, child: ElevatedButton( child: Text(ansText), style: ElevatedButton.styleFrom( shape: const StadiumBorder(), primary: isSelected ? Colors.orangeAccent : Colors.white, onPrimary: isSelected ? Colors.white : Colors.black, ), onPressed: () { setState(() { if (selectedOption == optionId) { // 取消选中 selectedOption = null; if (isCorrect) score--; } else { // 替换选中项,处理分数修正 if (selectedOption != null && questionList[currentQuestionIndex].correct_ans == selectedOption) { score--; } selectedOption = optionId; if (isCorrect) score++; } }); }, ), ); } else { return const SizedBox.shrink(); } } _nextButton() { bool isLastQuestion = currentQuestionIndex == questionList.length - 1; return Container( width: MediaQuery.of(context).size.width * 0.5, height: 48, child: ElevatedButton( child: Text(isLastQuestion ? 'Submit' : 'Next'), style: ElevatedButton.styleFrom( shape: const StadiumBorder(), primary: Colors.blueAccent, onPrimary: Colors.white, ), onPressed: () { if (isLastQuestion) { showDialog(context: context, builder: (_) => _showScoreDialog()); } else { setState(() { currentQuestionIndex++; selectedOption = null; // 切换题目清空选中状态 }); } }, ), ); } _showScoreDialog() { bool isPassed = score >= questionList.length * 0.6; String title = isPassed ? 'Passed' : 'Failed'; return AlertDialog( title: Text( '$title | Score is $score', style: TextStyle(color: isPassed ? Colors.green : Colors.redAccent), ), content: ElevatedButton( child: const Text('Restart'), onPressed: () { Navigator.pop(context); setState(() { currentQuestionIndex = 0; score = 0; selectedOption = null; }); }, ), ); } }
内容的提问来源于stack exchange,提问作者Milinda Jayasinghe
相关产品推荐
相关产品推荐

