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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 05:29:51