如何在Dart的onInit()执行前优先加载List值?Flutter Sqlite开发问题
问题根因
- 类实例化时会同步完成
_questions属性的赋值,此时异步的Sqlite查询还未执行,savedList是空数组,所以_questions从一开始就是空的,后续savedList更新也不会触发_questions重新计算 - GetX的
onInit方法不会等待内部异步操作执行完成再推进页面渲染,等你拿到Sqlite查询结果时,页面已经完成首次渲染了 - 你的
Body组件是StatelessWidget,却声明了initState方法,该方法永远不会被执行,属于语法错误
修复步骤
第一步:修改QuestionController
- 把
_questions改为响应式列表,新增加载状态标记,删除原有的固定初始化逻辑 - 调整
onInit执行顺序,优先完成数据库查询、题目数据赋值,再初始化动画、页面控制器等业务逻辑 - 新增数据去重逻辑,避免每次进入页面重复插入相同题目
修改后的核心代码如下:
class QuestionController extends GetxController with SingleGetTickerProviderMixin { static Database _database; static DbConn dbConn = DbConn(); AnimationController _animationController; Animation _animation; Animation get animation => this._animation; PageController _pageController; PageController get pageController => this._pageController; bool _isAnswered = false; bool get isAnswered => this._isAnswered; int _correctAns; int get correctAns => this._correctAns; int _selectedAns; int get selectedAns => this._selectedAns; RxInt _questionNumber = 1.obs; RxInt get questionNumber => this._questionNumber; int _numOfCorrectAns = 0; int get numOfCorrectAns => this._numOfCorrectAns; // 新增响应式题目列表和加载状态 RxList<Question> _questions = <Question>[].obs; List<Question> get questions => _questions; RxBool isLoading = true.obs; @override Future<void> onInit() async { super.onInit(); // 优先执行数据库操作 String databasesPath = await getDatabasesPath(); _database = await openDatabase( join(databasesPath, 'Question.db'), onCreate: (db, version) { debugPrint('called getQues inside DB table creation'); return db.execute( "CREATE TABLE QuesTable(id INTEGER, question TEXT, options TEXT, answer_index INTEGER)", ); }, version: 1, ); final Database db = _database; // 先查询表是否已有数据,避免重复插入 List<Map> existingList = await db.rawQuery('SELECT * FROM QuesTable'); if (existingList.isEmpty) { for (int i = 0; i < DbConn.sample_data.length; i++) { var exId = DbConn.sample_data[i]['id']; var exQuestion = DbConn.sample_data[i]['question']; var exOptions = DbConn.sample_data[i]['options']; var exAnswerIndex = DbConn.sample_data[i]['answer_index']; int insertId1 = await _database.rawInsert( 'INSERT INTO QuesTable(id, question, options, answer_index) VALUES(?, ?, ?, ?)', [exId, exQuestion, exOptions, exAnswerIndex] ); debugPrint('InsertedValue: $insertId1'); } existingList = await db.rawQuery('SELECT * FROM QuesTable'); } // 赋值题目列表 _questions.value = existingList.map((question) => Question( id: question['id'], question: question['question'], options: question['options'], answer_index: question['answer_index'] )).toList(); // 数据加载完成后再初始化动画、页面控制器 _animationController = AnimationController(duration: Duration(seconds: 30), vsync: this); _animation = Tween<double>(begin: 0, end: 1).animate(_animationController) ..addListener(() { update(); }); _pageController = PageController(); // 标记加载完成 isLoading.value = false; _animationController.forward().whenComplete(nextQuestion); } @override void onClose() { _animationController.dispose(); _pageController.dispose(); super.onClose(); } // 其余原有方法checkAns、nextQuestion、updateTheQnNum、checkSavedlist保留即可 }
第二步:修改Body组件
- 删除无效的
initState方法,若需要保留futureAlbum逻辑可以将Body改为StatefulWidget - 新增加载状态判断,数据加载中显示加载动画,加载完成再渲染题目页面
修改后的核心代码如下:
class Body extends StatelessWidget{ const Body({ Key key, }) : super(key: key); @override Widget build(BuildContext context) { QuestionController _questionController = Get.put(QuestionController()); return Obx(() { // 加载中显示加载动画 if (_questionController.isLoading.value) { return const Center(child: CircularProgressIndicator(color: kSecondaryColor)); } // 加载完成显示原有页面 return Stack( children: [ SvgPicture.asset("assets/icons/endless-constellation.svg", fit: BoxFit.fill), SafeArea( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.symmetric(horizontal: kDefaultPadding), child: ProgressBar(), ), SizedBox(height: kDefaultPadding), Padding( padding: const EdgeInsets.symmetric(horizontal: kDefaultPadding), child: Text.rich( TextSpan( text: "Question ${_questionController.questionNumber.value}", style: Theme.of(context) .textTheme .headline4 .copyWith(color: kSecondaryColor), children: [ TextSpan( text: "/${_questionController.questions.length}", style: Theme.of(context) .textTheme .headline5 .copyWith(color: kSecondaryColor), ), ], ), ), ), Divider(thickness: 1.5), SizedBox(height: kDefaultPadding), Expanded( child: PageView.builder( physics: NeverScrollableScrollPhysics(), controller: _questionController.pageController, onPageChanged: _questionController.updateTheQnNum, itemCount: _questionController.questions.length, itemBuilder: (context, index) => QuestionCard( question: _questionController.questions[index], ), ), ), ], ), ) ], ); }); } }
内容的提问来源于stack exchange,提问作者Terin Tittu
相关产品推荐
相关产品推荐

