エンジニアとして働いていると、「覚えておきたいこと」は際限なく増えていく。資格試験の勉強、英語の勉強。手帳へメモ。それらをノートやアプリに書き留めても、次に見返さない。そういう経験は誰にでもあるのではないでしょうか?
続かない理由は意志の弱さではなく、ハードルの高さだと思っている。「まとまった時間を確保して、集中して復習する」という前提設計のツールが多すぎる。エンジニアの日常にそんな時間は簡単には取れない。
そこで、1日数秒で終わるフラッシュカード学習アプリを作った。この記事ではアプリの紹介と、実装の中で工夫した「連続学習日数(ストリーク)の計算ロジック」を解説する。
アプリでできること
Cobo Memo は iOS / Android で動作する無料のフラッシュカード暗記アプリだ。
カードの登録
覚えたい内容を自分でカードとして登録するのが基本だが、それが面倒なら AI カード生成が使える。テキストを貼り付けると Google Gemini がカードを自動生成してくれる。資料のスクリーンショットから OCR でテキストを読み取ってカード化することも可能だ。
他のユーザーが公開している共有デッキをダウンロードするという手もある。AWS、Linux コマンド、英単語など、よく使われるカテゴリはすでに誰かが作っている。
学習体験
学習画面はシンプルに設計してある。間隔反復アルゴリズムによって、苦手なカードほど頻繁に出題される。
疲れた日ややる気が出ない日は、1枚だけ学習して「今日も触ったぞ」という感覚を積み重ねることが目的なので、完璧主義にならなくていい設計にしている。1枚でも学習すると連続学習日数が更新されるゆとり仕様です(笑)やる気がある日にだけ頑張るようにしています。継続することに価値があり!
機能について
過去記事に掲載
いますぐためす
実装の話: さらなる甘やかし機能
せっかくなので、実装で工夫したポイントを1つ紹介する。
「1日忘れてもリカバリー可能」な仕様
連続学習日数(ストリーク)は多くの習慣化アプリが採用している機能だが、よくある実装の問題点がある。1日でも空くと即座にゼロに戻る、という厳格な設計だ。
Cobo Memoでは次のルールにした。
- 今日か昨日まで連続していれば、ストリークは継続
- 2日以上空いた場合のみリセット
つまり昨日サボっても、今日学習すれば連続扱いになる。何ともゆとり仕様ですが、私のモチベーションを継続するためこの仕様にしています。
これがどうコードに落ちているか見てほしい。
現在の連続学習日数を計算するロジック
// lib/function/utility/db_helper.dart
Future<int> calculateConsecutiveLearningDays(String userId) async {
final allSummaries = await getAllLearningSummariesLocal();
// ユーザーの DAILY サマリーのみ抽出
final userSummaries = allSummaries
.where((s) => s.userId == userId && s.summaryType == 'DAILY')
.toList();
// 日付ごとの学習時間を集計
Map<String, int> dailyLearningTime = {};
for (var summary in userSummaries) {
String dateKey = summary.dateKey; // "YYYY-MM-DD" 形式
dailyLearningTime[dateKey] =
(dailyLearningTime[dateKey] ?? 0) + (summary.totalLearningTime ?? 0);
}
// 学習時間が 1 秒以上ある日だけを対象にする
List<String> learningDates = dailyLearningTime.entries
.where((e) => e.value > 0)
.map((e) => e.key)
.toList()
..sort((a, b) => b.compareTo(a)); // 降順ソート
if (learningDates.isEmpty) return 0;
DateTime now = DateTime.now();
final todayStr = now.toIso8601String().substring(0, 10);
// ポイント: 今日学習済みなら「今日」から、未学習なら「昨日」から遡る
DateTime checkDate = learningDates.contains(todayStr)
? now
: now.subtract(const Duration(days: 1));
int consecutiveDays = 0;
for (int i = 0; i < 9999; i++) {
String checkDateStr = checkDate.toIso8601String().substring(0, 10);
if (learningDates.contains(checkDateStr)) {
consecutiveDays++;
checkDate = checkDate.subtract(const Duration(days: 1));
} else {
break; // 連続が途切れた時点で終了
}
}
return consecutiveDays;
}
注目すべきは checkDate の初期値の決め方だ。
DateTime checkDate = learningDates.contains(todayStr)
? now
: now.subtract(const Duration(days: 1));
今日まだ学習していなくても、昨日から遡ることでストリークを生かしておく。昨日学習した記録があれば consecutiveDays はそこからカウントされ、今日学習するとさらに伸びる。2日以上空いていれば break がすぐに発動してゼロになる。
UI 側での通知処理
ホーム画面側では、ストリークの更新をトリガーにダイアログを出す処理がある。
// lib/function/home/home_screen.dart
Future<void> _checkAndUpdateConsecutiveDays() async {
final today = DateTime.now().toIso8601String().substring(0, 10);
final lastLearningDate = _prefs.getString(_lastLearningDateKey) ?? '';
// 今日すでにチェック済みならスキップ
if (lastLearningDate == today) return;
final hasLearnedToday = await DBHelper().hasLearnedToday(userIdentifier);
if (!hasLearnedToday) return;
final currentStreak =
await DBHelper().calculateConsecutiveLearningDays(userIdentifier);
final previousStreak = _prefs.getInt(_lastConsecutiveDaysKey) ?? 0;
await _prefs.setString(_lastLearningDateKey, today);
await _prefs.setInt(_lastConsecutiveDaysKey, currentStreak);
if (currentStreak > previousStreak && currentStreak > 1) {
// 連続記録が更新されたことをポップアップで通知
_showConsecutiveDaysUpdateDialog(currentStreak);
} else if (currentStreak == 1 && previousStreak == 0) {
// 学習を再開したことを通知
_showLearningRestartDialog();
}
}
SharedPreferences でローカルにキャッシュすることで、毎回 DB を読みに行かずに済む。今日すでに処理済みなら即 return するので、ホーム画面を何度開いてもオーバーヘッドはほぼない。
なぜ「完璧を求めない」設計にしたか
継続系アプリが嫌われるとき、大抵こういう瞬間がある。
旅行やら残業やらで1日だけ触れなかった日があり、翌朝アプリを開いたら連続記録がゼロになっている。あの絶望感は経験した人にはわかると思う。「もうどうでもいいか」という気持ちになって、そのままアプリを削除する流れ。
学習の本質は記憶の定着であって、連続記録の維持ではない。ストリークは継続のモチベーションを補助する道具であるべきで、プレッシャーをかける道具であってはいけない。
1日の猶予を持たせることで、「昨日やれなかったけど今日やっておこう」という自然な行動が生まれやすくなる。完璧にやれない日があっても、そこで止まらず続けることの方が長期的には重要だという判断だ。
同じ思想は学習体験全体にも通じている。1セッションのカード枚数を少なく設定できるのも、「今日は3枚だけ」で済ませることを許容するためだ。毎日100枚より、毎日5枚を1年続ける方が確実に記憶に残る。
継続は力です
資格試験も、新しい技術のキャッチアップも、続けること自体が一番難しい。ハードルを下げ、1日数秒でも触れる習慣を作ることが、長期的な知識の積み上げにつながる。
Cobo Memo はそのための道具として作った。学習ステータスの完全定着を目指して、無理のない暗記学習を続けてほしい。
おわりに
最後まで読んでいただきありがとうございました。
COBO MEMOの開発進捗や、個人開発・資格学習についての発信をXでしています。
よかったらフォローしてください。
- X: @CoboMemo
フィードバックや機能リクエストはコメント欄へ記入ください!

