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

Flutter报LateInitializationError:questionsSnapshot未初始化

问题说明

这是一段基于Flutter框架开发的移动端应用业务代码,核心功能为对接Cloud Firestore拉取指定quizId对应的测验题目,完成测验页面渲染。
代码运行时抛出异常:
LateError (LateInitializationError: Field 'questionsSnapshot' has not been initialized.)
问题根因:代码中使用late修饰的QuerySnapshot类型字段questionsSnapshot为异步赋值,页面首次执行build方法时异步数据尚未返回、字段未完成初始化就被访问,最终触发运行时错误。

完整问题代码
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:flutter_application_testtt/models/question_model.dart';
import 'package:flutter_application_testtt/screens/constants.dart';
import 'package:flutter_application_testtt/screens/reusable_widgets/quiz_play_widget.dart';
import 'package:flutter_application_testtt/services/database.dart';

class GameQuiz extends StatefulWidget {
  final String quizId;
  GameQuiz(this.quizId);

  @override
  State<GameQuiz> createState() => _GameQuizState();
}

int total = 0;
int _correct = 0;
int _incorrect = 0;
int _notAttempted = 0;

class _GameQuizState extends State<GameQuiz> {
   late QuerySnapshot questionsSnapshot;
   DatabaseService databaseService = new DatabaseService();
  QuestionModel getQuestionModelFromDatasnapshot(DocumentSnapshot questionSnapshot) {
    QuestionModel questionModel = new QuestionModel();

    questionModel.question = questionSnapshot["question"];

    List<String> options = [
      questionSnapshot["option1"],
      questionSnapshot["option2"],
      questionSnapshot["option3"],
      questionSnapshot["option4"],
    ];
    options.shuffle();

    questionModel.option1 = options[0];
    questionModel.option2 = options[1];
    questionModel.option3 = options[2];
    questionModel.option4 = options[3];
    questionModel.correctOption = questionSnapshot["option1"];
    questionModel.answered = false;

    return questionModel;
  }

  @override
  void initState() {
    super.initState();
    print("${widget.quizId}");
  
    databaseService.getQuizData(widget.quizId).then((value) {
      questionsSnapshot = value;
      _notAttempted = 0;
      _correct = 0;
      _incorrect = 0;
      total = questionsSnapshot.docs.length;

      print("$total this is total");
      setState(() {});
    });
    
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text('QUIZ'),
          backgroundColor: background,
          shadowColor: Colors.transparent,
        ),
        body: Container(
          child: Column(
            children: [
              questionsSnapshot.docs.length==0?
                   const SizedBox.shrink()
                  : ListView.builder(
                      shrinkWrap: true,
                      physics: ClampingScrollPhysics(),
                      itemCount: questionsSnapshot.docs.length,
                      itemBuilder: (context, index) {
                        return QuizPlayTile(
                          questionModel:getQuestionModelFromDatasnapshot(questionsSnapshot.docs[index]), 
                          index: index,
                        );
                      },
                    )
            ],
          ),
        ));
  }
}

class _questionsSnapshot {
}

class QuizPlayTile extends StatefulWidget {
  final QuestionModel questionModel;
  final int index;
  QuizPlayTile({required this.questionModel, required this.index});

  @override
  State<QuizPlayTile> createState() => _QuizPlayTileState();
}

class _QuizPlayTileState extends State<QuizPlayTile> {
  String optionSelected = "";

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Column(
        children: [
          Text(widget.questionModel.question),
          SizedBox(
            height: 4,
          ),
          OptionTile(
            correctAnswer: widget.questionModel.option1,
            description: widget.questionModel.option1,
            option: "A",
            OptionSelected: optionSelected,
          ),
          SizedBox(
            height: 4,
          ),
          OptionTile(
            correctAnswer: widget.questionModel.option1,
            description: widget.questionModel.option2,
            option: "B",
            OptionSelected: optionSelected,
          ),
          SizedBox(
            height: 4,
          ),
          OptionTile(
            correctAnswer: widget.questionModel.option1,
            description: widget.questionModel.option3,
            option: "C",
            OptionSelected: optionSelected,
          ),
          SizedBox(
            height: 4,
          ),
          OptionTile(
            correctAnswer: widget.questionModel.option1,
            description: widget.questionModel.option4,
            option: "D",
            OptionSelected: optionSelected,
          )
        ],
      ),
    );
  }
}
修复方案

核心修复逻辑:异步数据加载过程中增加状态判断,避免未初始化就访问late字段,同时给用户明确的加载状态反馈。

  • 去掉字段的late修饰,将questionsSnapshot改为可空类型,新增isLoading布尔值标记数据加载状态
  • 数据未加载完成时,页面显示加载进度组件,不访问questionsSnapshot字段
  • 数据加载完成后更新isLoading状态,触发页面重新渲染,再渲染题目列表
  • 代码中无实际作用的空类_questionsSnapshot可以直接删除
  • 全局定义的total、_correct、_incorrect、_notAttempted变量建议挪到_GameQuizState类内部,避免多页面打开时数据互相覆盖

核心修改后的代码片段:

class _GameQuizState extends State<GameQuiz> {
  QuerySnapshot? questionsSnapshot;
  bool isLoading = true;
  // 统计变量挪到类内部
  int total = 0;
  int _correct = 0;
  int _incorrect = 0;
  int _notAttempted = 0;
  DatabaseService databaseService = DatabaseService();

  // 省略原有getQuestionModelFromDatasnapshot方法

  @override
  void initState() {
    super.initState();
    print("${widget.quizId}");
    databaseService.getQuizData(widget.quizId).then((value) {
      questionsSnapshot = value;
      _notAttempted = 0;
      _correct = 0;
      _incorrect = 0;
      total = questionsSnapshot!.docs.length;
      isLoading = false;
      print("$total this is total");
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: const Text('QUIZ'),
          backgroundColor: background,
          shadowColor: Colors.transparent,
        ),
        body: Container(
          child: Column(
            children: [
              isLoading
                ? const Center(child: CircularProgressIndicator())
                : questionsSnapshot?.docs.length == 0
                  ? const Center(child: Text("暂无测验题目"))
                  : ListView.builder(
                      shrinkWrap: true,
                      physics: const ClampingScrollPhysics(),
                      itemCount: questionsSnapshot!.docs.length,
                      itemBuilder: (context, index) {
                        return QuizPlayTile(
                          questionModel:getQuestionModelFromDatasnapshot(questionsSnapshot!.docs[index]), 
                          index: index,
                        );
                      },
                    )
            ],
          ),
        ));
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 01:33:20