dart_srs 1.0.1
dart_srs: ^1.0.1 copied to clipboard
Pluggable spaced repetition engine with FSRS-6 scheduling, study-type adapters, optional review logging, and session support. Storage-agnostic (bring your own CardStateStore).
dart_srs #
A pluggable spaced repetition (SRS) engine for Dart (no Flutter SDK required). Use it from Flutter apps, servers, or CLI tools. It ships with an FSRS-6-style scheduler, study-type adapters (flashcards, MCQ, typing, true/false), optional review logging, and a study session helper for linear review flows.
The package is storage-agnostic: you implement CardStateStore (SQLite, Hive, REST, etc.) and plug it into SRSEngine.
Repository: github.com/amritmalla/dart_srs
Requirements #
- Dart SDK ^3.10.7 (see
pubspec.yaml)
Installation #
Add dart_srs to your project:
dart pub add dart_srs
This will add a line to your package's pubspec.yaml (and run dart pub get):
dependencies:
dart_srs: ^1.0.1
Alternatively, you can depend on Git directly:
dependencies:
dart_srs:
git:
url: https://github.com/amritmalla/dart_srs.git
ref: main
Features #
SRSAlgorithm— implement your own scheduler;FSRS6Algorithmis the built-in default withFSRS6Config(21 weights, learning/relearning steps, desired retention, fuzzing, etc.).SRSEngine<T>— coordinates algorithm +StudyTypeAdapter<T>+CardStateStore; maps typed outcomes toSRSRatingand persistsSRSCardState.StudySession— queue-based session withRequeuingStrategy(default requeues Again and some Hard/Good learning cases).- Scheduling preview — human-readable intervals for all four ratings (
SchedulingPreview). - Optional
ReviewLogger— audit trail viaReviewLog(failures in logging are swallowed so reviews still complete). - Testing — import
package:dart_srs/testing.dartforInMemoryCardStateStore.
Quick start #
Full runnable sample: example/example.dart.
import 'package:dart_srs/dart_srs.dart';
import 'package:dart_srs/testing.dart';
Future<void> main() async {
final algorithm = FSRS6Algorithm(config: FSRS6Config());
final store = InMemoryCardStateStore();
await store.saveCardState(algorithm.getInitialCardState('word_1'));
final engine = SRSEngine<FlashcardOutcome>(
algorithm: algorithm,
adapter: const FlashcardAdapter(),
store: store,
);
final next = await engine.reviewCard(
'word_1',
const FlashcardOutcome(rating: SRSRating.good),
);
// Use next.dueDate, next.cardPhase, next.stability, etc.
}
Study session #
final session = engine.createSession(['id_a', 'id_b', 'id_c']);
await session.start();
while (session.state == SessionState.active) {
final card = session.currentCard!;
// Show card to the user, then:
await session.submitReview(SRSRating.good);
}
final summary = session.result; // stats, duration, cardsReviewed
Scheduling buttons (preview) #
final preview = await engine.getSchedulingPreview('word_1');
// preview.againInterval, .hardInterval, .goodInterval, .easyInterval (strings)
// preview.againDays, … (numeric days from now)
Architecture #
| Piece | Role |
|---|---|
SRSAlgorithm |
Given current SRSCardState + SRSRating, returns the next state. |
SRSEngine |
Loads state from store, runs adapter + algorithm, saves state, optional logging. |
StudyTypeAdapter<T> |
Maps your UI outcome type T to SRSRating (Again / Hard / Good / Easy). |
CardStateStore |
Persistence for SRSCardState (due queries, bulk load by id, etc.). |
StudySession |
In-memory queue over a fixed id list; calls algorithm + store per rating. |
Ratings align with common SRS UIs: SRSRating.again, hard, good, easy. Card lifecycle phases are CardPhase: newCard, learning, review, relearning.
Built-in adapters and outcomes #
| Adapter | Outcome type | Notes |
|---|---|---|
FlashcardAdapter |
FlashcardOutcome |
User picks the rating directly. |
MCQAdapter |
MCQOutcome |
Maps correctness + response time + option count to a rating. |
TypingAdapter |
TypingOutcome |
Uses TypingAccuracy (exact / close / wrong). |
TrueFalseAdapter |
TrueFalseOutcome |
Similar heuristics to MCQ-style timing. |
Implement StudyTypeAdapter<MyOutcome> for custom modalities.
Configuration (FSRS-6) #
FSRS6Config validates on construction:
weights— exactly 21 doubles (defaults inFSRS6Constants.defaultWeights).desiredRetention— in(0.0, 1.0); default0.9.learningSteps/relearningSteps— positive minute-based steps (see defaults inFSRS6Constants).maximumInterval,graduatingInterval,easyInterval,enableFuzzing.
Invalid values throw InvalidConfigurationException.
Implementing storage #
Implement CardStateStore with your database or backend. You must support:
getCardState/saveCardState/saveCardStatesgetDueCards(DateTime now)— cards that are not new anddueDateis beforenowgetNewCards(int limit)— cards inCardPhase.newCardgetCardsByIds— order should match how you want the session queue built (the session uses the list returned; missing ids are omitted)deleteCardState
See the doc/ folder for split guides (storage, engine vs session, adapters, FSRS-6, testing).
Testing #
import 'package:dart_srs/testing.dart';
final store = InMemoryCardStateStore();
store.seed([/* SRSCardState ... */]);
store.clear();
InMemoryCardStateStore is intended for tests and demos, not production persistence.
Exceptions #
| Type | Typical cause |
|---|---|
CardNotFoundException |
Engine asked for a missing cardId. |
SessionStateException |
Invalid session transition (e.g. submit after complete). |
InvalidConfigurationException |
Bad FSRS6Config. |
Documentation #
- doc/README.md — index of topic guides (overview, installation, architecture, storage, engine/session, adapters, FSRS-6, testing, exceptions).
- API reference — run
dart docin this package; HTML is emitted underdoc/api/.
Development #
dart pub get
dart analyze
dart test
dart run example/example.dart
References #
- FSRS family of algorithms (weights and behavior are configured via
FSRS6Config; tune weights for your dataset when optimizing retention).