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!;
}
use singleton pattern to avoid creating new instance each time. instead of using