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

Flutter题库优化需求:解决JSON重复加载与按钮选中变色问题

Flutter题库功能优化方案

一、解决重复加载JSON数据问题

问题原因

当前代码中FutureBuilder的future参数直接传入getQuestionData(),每次组件build(比如切换题目调用setState时)都会重新执行网络请求,导致重复加载数据。

解决方案

在initState生命周期中一次性加载所有题目数据,加载完成后更新questionList并刷新界面,不再依赖FutureBuilder重复发起请求。

修改要点:

  1. 新增isLoading变量控制加载状态UI显示;
  2. 在initState中调用数据加载方法,完成后更新列表并关闭加载状态;
  3. 移除FutureBuilder,直接根据isLoading渲染对应界面。

二、实现选项按钮选中变色交互

问题原因

当前控制按钮颜色的click是局部变量,每次build都会重置,无法保留选中状态;同时缺少记录当前选中选项的全局变量。

解决方案

新增变量记录当前选中的选项标识(如'1'/'2'/'3'/'4'),点击按钮时更新该变量,根据选项是否被选中动态设置按钮样式,同时处理得分逻辑避免重复加分。

修改要点:

  1. 新增selectedOption变量存储当前选中的选项序号;
  2. 在按钮组件中根据selectedOption判断选中状态,动态调整颜色;
  3. 点击按钮时更新选中状态,同步处理分数增减。

修改后的完整代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 23:30:17