reading_progress 0.0.1 copy "reading_progress: ^0.0.1" to clipboard
reading_progress: ^0.0.1 copied to clipboard

A pure Dart engine for verified reading progress — confident-word milestones, active-dwell tracking, and reading-speed calibration.

reading_progress #

A pure Dart engine for verified reading progress. Feed it a book's word-density map and a stream of reading events; it tells you what counts as real reading, when a milestone is reached, and when a book is genuinely complete.

Why #

Time-on-page lies — a page left open for an hour is not an hour of reading. This engine:

  • counts active engaged time from a presence heartbeat — periodic Ticks plus interactions credit time only while the reader is present (within an idle cutoff of the last page-open/interaction), so silent reading counts but walking away does not;
  • gates page completion on engaged dwell vs an expected read time from a per-book calibrated reading speed (EMA, seeded from the Brysbaert 2019 meta-analysis: 238 wpm non-fiction / 260 fiction, 240 unknown);
  • rejects skim-scrolling, and requires a forward page-turn to complete;
  • counts progress in confident words, not pages — format-agnostic and honest.

Confident-word model #

Progress is measured in words actually read:

  • a page contributes its words only once it passes the dwell gate (deduped — re-reading never double-counts);
  • every wordUnitSize confident words banks a point;
  • milestoneThresholdUnits points reaches a milestone.

A skipped page contributes 0 words, so it just delays the next milestone — never blocks it. Book completion is strict: every content page must be read.

Architecture #

Functional core, imperative shell. Everything here is the pure core — a single reducer:

final out = reduce(state, event, deps); // (state', effects)
  • ReadingEventPageOpened, Interaction, Tick, PageExited, AppBackgrounded, ScreenOff
  • ProgressEffectPageCompleted, PointBanked, MilestoneReached, BookCompleted
  • Swappable strategies — ReadingSpeedModel, EngagementPolicy, CompletionPolicy

Partial per-page dwell is stashed and resumed across revisits — glancing ahead and returning never resets a page.

Usage #

import 'package:reading_progress/reading_progress.dart';

final deps = ReadingDeps(
  density: const BookDensity(
    pageWords: [120, 600, 950, 30, 1100],
    skippablePages: {3},
    genre: Genre.nonFiction,
  ),
);

var state = ReadingState.initial(deps);

void onEvent(ReadingEvent event) {
  final out = reduce(state, event, deps);
  state = out.state;
  for (final effect in out.effects) {
    switch (effect) {
      case PointBanked(:final point, :final wordsRead):
        print('point $point ($wordsRead words)');
      case MilestoneReached(:final index, :final wordsRead):
        print('milestone $index at $wordsRead words');
      case BookCompleted():
        print('done: ${(state.progressByWords(deps.density) * 100).round()}%');
      case PageCompleted(:final page):
        print('page $page verified');
    }
  }
}

The shell feeds real signals as events: PageOpened/PageExited on navigation, Interaction on tap/scroll, a Tick ~every 10s while the reader is foreground + screen-on, and AppBackgrounded/ScreenOff on lifecycle changes.

ReadingState is JSON-friendly value data (wordsRead, pointsBanked, milestonesReached, sessionEngagedMs, wpm, bitmaps, partials) — persist it however you like and rehydrate to resume. Derived getters: progressByWords(density), isCalibrating(config), totalMilestones(density, config).

Tuning #

Every threshold lives in ProgressConfig: wordUnitSize, milestoneThresholdUnits, calibrationUnits, idleCutoffMs, maxStepMs, k, alpha, WPM seeds + clamp, skim thresholds. The host passes its own — the engine never reads a literal:

ReadingDeps(
  density: density,
  config: const ProgressConfig(
    wordUnitSize: 250,
    milestoneThresholdUnits: 5,
    calibrationUnits: 2,
    k: 0.5,
  ),
);

For full algorithm control, supply custom ReadingSpeedModel / EngagementPolicy / CompletionPolicy implementations to ReadingDeps instead of the defaults.

Example #

See example/ for an interactive Flutter harness — buttons for reading silently (ticks), tapping, slow-scroll, skimming, idling, backgrounding, and page navigation, with live ReadingState (words read, points, milestones, calibrated wpm, progress) and an effect log.

License #

MIT — see LICENSE.

0
likes
160
points
0
downloads

Documentation

API reference

Publisher

verified publishercodeswot.dev

Weekly Downloads

A pure Dart engine for verified reading progress — confident-word milestones, active-dwell tracking, and reading-speed calibration.

Repository (GitHub)
View/report issues

Topics

#reading #progress #engagement #ereader #tracking

License

MIT (license)

Dependencies

equatable

More

Packages that depend on reading_progress