Flutter · Android & iOS

Remember
what matters.

DYF (Do You Forget) is a distraction-free reminder app with an intelligent notification engine that stays quiet when you have time — and pulses more urgently as your deadline closes in.

Open source · No accounts · No trackers · No cloud

DYF running on Android: main screen showing garden progress bar and today's tasks

The core algorithm

Adaptive Pulse Scheduler

APS distributes notifications geometrically across the time window between task creation and deadline. The parameter k controls how aggressively alerts cluster near the end — higher priority means steeper concentration. Interact with the controls below to see the live distribution.

Priority (P)

Days Remaining (D)

14 days

Density Curve k = 2.0

Notification Timeline

Total Alerts
iOS Slots Used
First Alert
Last Alert

Core Formula

τᵢ = ln ( 1 + (i/N) × (ek − 1) ) / k
2.0= k (concentration) = N (budget) 14= D (days)

What makes DYF different

Designed to stay
out of your way

Gesture-Based Theme

No toggle. No settings. Swipe left or right anywhere across the top bar area and the app instantly switches between light and dark mode. A brief haptic confirms it — then it disappears, like it was never there.

Requires >70 px drag distance or >600 px/s fling velocity — so accidental scrolling never triggers it.

Voice Input via RegExp

Speak naturally. The OS transcribes on-device, and a pure-Dart RegExp engine extracts task, date, and time with surgical precision — zero AI cloud calls, zero model downloads, zero JNI crashes.

"Remind me to call the dentist tomorrow at 3 PM"

📍 Call the dentist 📅 Tomorrow ⌛ 3:00 PM

Replaced google_mlkit_entity_extraction — eliminating the native JNI crash on Android 16.

Source code discoveries

Two things worth
a closer look

Two architectural decisions buried in the Flutter codebase that reflect a bias toward surgical precision over brute force.

01
Zero-Affordance Theme Toggle
task_header.dart — _HiddenThemeSwipeZone
// Fires only on deliberate, heavy swipes
void _onDragEnd(DragEndDetails details) {
  final velocity = details.primaryVelocity ?? 0;

  final isDeliberate =
    _accumulatedDelta.abs() >= 70.0   // px threshold
    || velocity.abs()       >= 600.0;  // px/s threshold

  if (isDeliberate) {
    HapticFeedback.mediumImpact();
    ThemeController.toggle();
  }

  _accumulatedDelta = 0;
}
A fully transparent GestureDetector wraps only the subtitle and progress bar — no button, no icon, no visual hint. Two guard conditions (distance ≥70 px or velocity ≥600 px/s) ensure a normal scrolling gesture never accidentally triggers a theme change. The haptic feedback is the only signal the user gets.
02
Index-Precise NLP Extraction
nlp_entity_extractor.dart — _removeSpans()
// Track exact char spans — not replaceFirst()
String _removeSpans(
  String text, List<_Span> spans) {

  final sorted = [...spans]
    ..sort((a, b) => a.start.compareTo(b.start));

  final buffer = StringBuffer();
  var cursor = 0;

  for (final s in sorted) {
    buffer.write(text.substring(cursor, s.start));
    buffer.write(' ');      // placeholder gap
    cursor = s.end;
  }
  return buffer.toString().trim();
}
Instead of replaceFirst() — which silently removes the wrong occurrence when a word repeats — the engine records the exact [start, end] character indices of every extracted datetime token, then surgically removes them in one sorted pass. A downstream sweep then cleans up orphaned prepositions like "at", "on", and "by" that get stranded after their date expressions are cut.
Available Now

Give it a try.
Your garden awaits.

Free, open source, and built with one purpose: making sure you actually remember.

Split APKs for minimal install size  ·  View source on GitHub ↗