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

Flutter问答App:点击ListTile无法切换下一题,静态变量修改无效

Flutter问答App页面切换无响应问题解决

问题描述

我是Flutter新手,正在开发一款问答App,App会在屏幕上展示一道题目及4个选项(共5道题),期望用户点击ListTile选择答案后,自动切换到下一题及对应选项。我尝试创建Info类型的screens列表,通过Info构造函数初始化题目与选项,初始页面为Info.screens[Info.indx],并试图在ListTile的onTap回调中使用setState递增index来实现页面切换,但操作后无任何变化,甚至onTap内的print语句也未输出到调试控制台。

问题根源

  1. 根组件未监听索引变化:Quizz作为根组件,其build方法直接返回Info.screens[Info.indx],但Quizz的状态未关联Info.indx的更新,即使索引改变,根组件也不会重新构建,页面自然无法切换。
  2. StatefulWidget使用不当:将Info设计为StatefulWidget并存储全局静态索引,会导致状态混乱——每个Info实例都是独立页面,当前setState仅能刷新自身,无法触发根组件重建。
  3. onTap逻辑错误:部分onTap中的setState写法无效(比如第一个ListTile里的Info.indx ;无实际作用),且实例变量widget.i与全局索引的关联逻辑混乱。

修复方案

将索引状态移到根组件Quizz中管理,Info改为无状态组件专注UI渲染,点击选项后通知根组件更新索引实现页面切换。

1. main.dart 文件

import 'package:flutter/material.dart';
import 'Info.dart';

void main(List<String> args) {
  runApp(const Quizz());
}

class Quizz extends StatefulWidget {
  const Quizz({super.key});

  @override
  State<Quizz> createState() => _QuizzState();
}

class _QuizzState extends State<Quizz> {
  int currentIndex = 2; // 初始显示第3题(索引从0开始)

  // 切换下一题,添加边界防止越界
  void goToNextQuestion() {
    setState(() {
      if (currentIndex < Info.screens.length - 1) {
        currentIndex++;
      } else {
        // 最后一题可重置或添加完成提示
        currentIndex = 0;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.indigo,
      ),
      home: Info(
        questionData: Info.screens[currentIndex],
        onAnswerSelected: goToNextQuestion,
      ),
    );
  }
}

2. Info.dart 文件

import 'package:flutter/material.dart';

// 单独抽离题目数据模型,逻辑更清晰
class QuestionData {
  final String question;
  final String option1;
  final String option2;
  final String option3;
  final String option4;

  QuestionData({
    required this.question,
    required this.option1,
    required this.option2,
    required this.option3,
    required this.option4,
  });
}

class Info extends StatelessWidget {
  final QuestionData questionData;
  final VoidCallback onAnswerSelected;

  // 预定义所有题目数据
  static List<QuestionData> screens = [
    QuestionData(question: "qst1", option1: "a", option2: "b", option3: "c", option4: "d"),
    QuestionData(question: "qst2", option1: "a", option2: "b", option3: "c", option4: "d"),
    QuestionData(question: "qst3", option1: "a", option2: "b", option3: "c", option4: "d"),
    QuestionData(question: "qst4", option1: "a", option2: "b", option3: "c", option4: "d"),
    QuestionData(question: "qst5", option1: "a", option2: "b", option3: "c", option4: "d"),
  ];

  const Info({
    super.key,
    required this.questionData,
    required this.onAnswerSelected,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          "Quizz App",
          style: TextStyle(fontSize: 20),
        ),
        centerTitle: true,
        backgroundColor: const Color.fromARGB(255, 75, 19, 15),
        shadowColor: Colors.black,
      ),
      body: Container(
        decoration: const BoxDecoration(
          color: Color.fromARGB(221, 165, 12, 12),
        ),
        child: Column(
          children: [
            Container(
              margin: const EdgeInsets.fromLTRB(10, 25, 10, 10),
              alignment: Alignment.center,
              child: Text(
                questionData.question,
                style: const TextStyle(color: Colors.white, fontSize: 21),
              ),
              height: 100,
              width: double.infinity,
              decoration: BoxDecoration(
                color: Colors.red,
                borderRadius: BorderRadius.circular(6),
                boxShadow: const [
                  BoxShadow(
                    color: Color.fromARGB(255, 93, 23, 18),
                    blurRadius: 20,
                  )
                ],
              ),
            ),
            const SizedBox(height: 30),
            Expanded(
              child: ListView(
                children: [
                  _buildOptionTile(questionData.option1),
                  _buildOptionTile(questionData.option2),
                  _buildOptionTile(questionData.option3),
                  _buildOptionTile(questionData.option4),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

  // 抽离选项Tile构建逻辑,减少重复代码
  Widget _buildOptionTile(String optionText) {
    return Container(
      margin: const EdgeInsets.all(10),
      decoration: BoxDecoration(
        color: Colors.red,
        borderRadius: BorderRadius.circular(6),
        boxShadow: const [
          BoxShadow(
            color: Color.fromARGB(255, 93, 23, 18),
            blurRadius: 20,
          )
        ],
      ),
      child: ListTile(
        onTap: () {
          print("选项$optionText被点击");
          onAnswerSelected();
        },
        mouseCursor: SystemMouseCursors.click,
        title: Center(
          child: Text(
            optionText,
            style: const TextStyle(color: Colors.white, fontSize: 21),
          ),
        ),
      ),
    );
  }
}

修复说明

  1. 状态上移:将当前题目索引currentIndex放在根组件Quizz的状态中,更新索引时通过setState触发根组件重建,实现页面切换。
  2. 数据与UI分离:新增QuestionData类存储题目数据,避免数据与UI组件耦合,代码结构更清晰。
  3. 无状态组件优化:Info改为StatelessWidget,仅负责接收数据和点击回调,专注UI渲染,状态管理交给上层组件。
  4. 代码复用:抽离_buildOptionTile方法减少重复代码,统一处理选项点击逻辑。
  5. 边界处理:切换索引时加入越界判断,避免出现数组访问错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.14 03:07:03