Drift/Moor and Flutter app - database streams with arguments

570 Views Asked by At

I have a quiz app with sqlite base. I'm using Drift (former Moor).

drift_db.dart (in a nutshell):

class QuestionsBase extends Table {
  IntColumn get questionnumber => integer()();
  TextColumn get question => text()();
  TextColumn get answera => text().nullable()();
  TextColumn get answerb => text().nullable()();
  TextColumn get answerc => text().nullable()();
  TextColumn get correctanswer => text()();
  TextColumn get categories => text()();
}

@DataClassName("QuestionRatingRow")
class QuestionsRating extends Table {
  IntColumn get questionnumber => integer()();
  IntColumn get questionrating => integer()();
  DateTimeColumn get repetitiondate => dateTime().nullable()();
}

...

//watching number of new questions
Stream<QueryRow> watchNumberOfNewQuestions(List<String> categories) {
  String queryString = 'SELECT COUNT(*) AS count FROM questions_rating '
      'LEFT JOIN questions_base ON questions_rating.questionnumber = questions_base.questionnumber '
      'WHERE (questions_rating.repetitiondate IS NULL) AND (questions_base.kategorie LIKE ';
  queryString += '\'%${categories[0]},%\' ';
  if (categories.length > 1) {
    for (var i = 1; i < categories.length; i++) {
      queryString +=
          'OR questions_base.kategorie LIKE \'%${categories[i]},%\' ';
    }
  }
  queryString += ');';
  return customSelect(queryString, readsFrom: {questionsRating}).watchSingle();
}

drift_repository.dart (in a nutshell):

import 'drift_db.dart';

class DriftRepository {
  late DLTDatabase dltDatabase;
  Stream<Map<String, dynamic>>? statisticsStream;
  Stream<Map<String, int>>? datesStream;
  Stream<int>? newQuestionsStream;

...

//unknown questions number stream
Stream<int> watchQuestionsNumber(List<String> categories) {
  if (newQuestionsStream == null) {
    final stream = dltDatabase.watchNumberOfNewQuestion(categories);
    newQuestionsStream = stream.map((row) {
      return row.data['count'];
    });
  }
  return newQuestionsStream!;
}
}

Code works fine, but when user changes question category (categories), nothing happens (only after restarting the application).

Of course, I know the problem is in this part:

if (newQuestionsStream == null) {

I can remove it and code works just fine. But it calls the method watchNumberOfNewQuestions(List categories), then saves an instance so I don’t create multiple streams. So far I've used streams in methods without arguments, so it was a good solution (I think). Now I have no idea what to do with this case.

Bonus question:-)

I have another stream Stream<Map<String, int>> watchDates(List categories). It returns quiz repetition dates (number of repetitions of questions for today, tomorrow, etc.). In addition to the problem described above, I am facing the fact that information about dates does not refresh after midnight. I have an idea to add another argument to watchDates: Stream<Map<String, int>> watchDates(List categories, DateTime dateTime) and then in the widget use Timer:

Timer.periodic(const Duration(hours: 1), (timer) {
  var dateTime = DateTime.now();
});

Is it a good idea?

EDIT: My solution

Since my app users very rarely change the category of questions (this assumes a usage scenario), I added a variable that stores a list of previous categories. When watchQuestionsNumber(List categories) is called, I check if the categories passed as an argument are different from the previous ones, and only then I return a new stream.

class DriftRepository {
  late DLTDatabase dltDatabase;
  Stream<Map<String, dynamic>>? statisticsStream;
  Stream<Map<String, int>>? datesStream;
  Stream<int>? newQuestionsStream;
  List<String> _newQuestionsStreamLastCategories = [];

...

  //unknown questions number stream
  Stream<int> watchQuestionsNumber(List<String> categories) {
    var _areCategoriesChanged =
        !listEquals(categories, _newQuestionsStreamLastCategories);
    if (newQuestionsStream == null || _areCategoriesChanged) {
      final stream = dltDatabase.watchNumberOfNewQuestion(categories);
      newQuestionsStream = stream.map((row) {
        return row.data['count'];
      });
      _newQuestionsStreamLastCategories = categories;
    }
    return newQuestionsStream!;
  }
1

There are 1 best solutions below

0
mohannadalnono On

use singleton pattern to avoid creating new instance each time. instead of using

if (newQuestionsStream == null) {