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

