Flutter问答应用实现题目不重复展示的技术方案求助
解决Flutter问答应用题目重复展示的可行方案
核心思路
按分类维护两个数据集合:
- 待作答队列:存储当前分类下还未被用户作答过的题目qnumber
- 已作答集合:存储当前分类下用户已经作答过的题目qnumber
每次从「待作答队列」中随机/顺序取题,用户作答完成后将该qnumber移至「已作答集合」;当「待作答队列」为空时,把「已作答集合」的所有qnumber转回「待作答队列」,实现该分类题目的循环展示(确保所有题都答过一遍才重复)。
具体实现步骤(结合Hive修复方案)
你之前用Hive未成功,大概率是没有按分类隔离状态、未处理队列重置逻辑,或者Hive存储/读取流程有问题,以下是修正后的完整方案:
1. 定义Hive存储模型
创建对应分类作答状态的Hive对象,用于持久化存储每个分类的待作答/已作答qnumber:
import 'package:hive/hive.dart'; part 'category_answer_state.g.dart'; // 需生成适配器 @HiveType(typeId: 0) class CategoryAnswerState extends HiveObject { @HiveField(0) String category; @HiveField(1) List<String> pendingQNumbers; @HiveField(2) Set<String> answeredQNumbers; CategoryAnswerState({ required this.category, required this.pendingQNumbers, required this.answeredQNumbers, }); }
运行命令生成适配器:
flutter packages pub run build_runner build
2. 初始化Hive与题目分类分组
在应用启动时初始化Hive,并将JSON加载的题目按分类分组,同时初始化每个分类的作答状态:
// main.dart 初始化逻辑 void main() async { await Hive.initFlutter(); Hive.registerAdapter(CategoryAnswerStateAdapter()); final box = await Hive.openBox<CategoryAnswerState>('category_answer_states'); // 从JSON加载题目(你的原有逻辑) final questions = await loadQuestionsFromJson(); // 按分类分组题目 Map<String, List<Question>> categoryQuestions = {}; for (var q in questions) { categoryQuestions.putIfAbsent(q.category, () => []).add(q); } // 初始化每个分类的作答状态(仅当Hive中无该分类数据时) for (var category in categoryQuestions.keys) { if (!box.containsKey(category)) { final qNumbers = categoryQuestions[category]!.map((q) => q.qnumber).toList(); box.put(category, CategoryAnswerState( category: category, pendingQNumbers: qNumbers, answeredQNumbers: {}, )); } } runApp(const MyApp()); }
3. 修改QuestionProvider的取题逻辑
在你的QuestionProvider中,替换原有随机取题逻辑,改为基于分类作答状态的取题:
class QuestionProvider with ChangeNotifier { final Box<CategoryAnswerState> _answerStateBox = Hive.box<CategoryAnswerState>('category_answer_states'); late Map<String, List<Question>> _categoryQuestions; // 初始化分类题目映射(可从main传入或内部加载) void initCategoryQuestions(Map<String, List<Question>> categoryQuestions) { _categoryQuestions = categoryQuestions; notifyListeners(); } Question? getNextQuestion(String currentCategory) { final state = _answerStateBox.get(currentCategory); if (state == null || _categoryQuestions[currentCategory] == null) return null; // 待作答队列为空时,重置队列(将已作答题目全部转回待作答) if (state.pendingQNumbers.isEmpty) { state.pendingQNumbers.addAll(state.answeredQNumbers); state.answeredQNumbers.clear(); state.save(); // 同步到Hive持久化 } // 随机取一道待作答题目 final randomIndex = Random().nextInt(state.pendingQNumbers.length); final targetQNumber = state.pendingQNumbers.removeAt(randomIndex); state.answeredQNumbers.add(targetQNumber); state.save(); // 保存状态 // 根据qnumber返回对应题目 return _categoryQuestions[currentCategory]!.firstWhere((q) => q.qnumber == targetQNumber); } }
4. 在QuizPage中调用取题逻辑
在你的QuizPage或QuizBottomSheet中,切换分类或获取下一题时,调用上述getNextQuestion方法即可:
// 示例:获取当前分类的下一题 void loadNextQuestion() { final nextQuestion = _questionProvider.getNextQuestion(currentCategory); if (nextQuestion != null) { // 更新页面展示的题目 setState(() => currentQuestion = nextQuestion); } }
关键注意事项
- 分类隔离:必须严格按分类维护作答状态,不能全局存储已作答qnumber,否则会导致不同分类的题目互相限制展示
- 及时持久化:每次修改作答状态后必须调用
state.save(),避免应用重启后丢失状态 - 队列重置逻辑:必须处理待作答队列为空的情况,否则会出现无题可取的问题
内容的提问来源于stack exchange,提问作者RipTyde
相关产品推荐
相关产品推荐

