Stay sharp. Don't drift.
An anti-drowsiness focus timer for work and study sessions. Set an interval; when it expires, a math problem blocks the screen. You can't dismiss it without solving the problem β so even if you start dozing off, the app forces a periodic cognitive break.
Install Β· Features Β· How it works Β· Architecture Β· Build from source Β· Contributing
| Common focus tools | Focus Mate |
|---|---|
| β Play music and hope you don't drift | β Forces you to engage every interval |
| β Pomodoro timer with no engagement check | β Math challenge to prove you're still with it |
| β Silently buzzes while you doze | β Escalates to a continuous alarm if you don't respond |
| β All UI, no follow-through | β Unskippable challenges, real cognitive breaks |
The science is simple: drowsiness is reversible with a 10-second engagement break. A stretch, a sip of water, or solving a math problem. Focus Mate makes that break non-negotiable and cognitively active β perfect for coding sessions, deep work, or study marathons.
| Home (idle) | Home (focusing) | Math challenge |
|---|---|---|
![]() |
![]() |
![]() |
| Set interval, start session | Live countdown with progress ring | Math problem to solve |
| Settings | Escalation alarm | Editor (custom interval) |
|---|---|---|
![]() |
red pulsing UI + looping alarm | scroll wheel + manual editor |
- Customizable intervals β 1 to 240 minutes via scroll wheel or manual editor
- Math challenge β 5 difficulty tiers (single-digit β 3-digit arithmetic) on a 60-second grace period
- Anti-bypass lock β
PopScope+ wakelock + ongoing notification = can't swipe away or screen-off - Foreground service β math challenges survive backgrounding, app kill, even device reboot (Android 8+)
- 60-second grace period (configurable: 15s/30s/60s/120s) to solve the challenge normally
- Looping alarm + vibration kicks in if you don't respond β continues until you touch the screen
- Visual urgency β red pulsing background overlay + "TOUCH SCREEN" pill + "STOP ALARM & SUBMIT" button
- Bypasses Do Not Disturb β uses
USAGE_ALARMaudio attribute on Android
- Today stats β focused time, problems solved, alarms triggered
- Streak counter β consecutive days using the app
- Persistent settings β everything in
SharedPreferences, survives restart + reboot
- Custom two-tone alarm β procedurally generated C5-E5 ascending bell (no copyrighted assets)
- Aurora animated background β soft drifting gradients
- Plus Jakarta Sans + JetBrains Mono typography
- Haptic feedback on every interaction
- Material 3 design language with custom dark theme
T = 0 [Start focus session]
β
T = 0..N [Live countdown with progress ring]
β
T = N [Alert fires, challenge starts]
β
T = N..N+60 [User has 60s to solve math problem]
β
T = N+60 [If unsolved: ESCALATION]
ββ Looping alarm tone (C5-E5)
ββ Vibration pattern (400ms on, 200ms off)
ββ Red pulsing background
ββ "TOUCH SCREEN" indicator
β
[User touches screen OR types in answer]
β
ββ Alarm silences
ββ User gets another 60s to solve
β
[Correct answer submitted]
β
ββ Stats updated (focus time, problems solved)
ββ Next interval scheduled
ββ Return to T = 0..N
lib/
βββ main.dart # App entry, MaterialApp, timezone setup
βββ core/
β βββ math_problem.dart # MathProblem + MathProblemGenerator
β βββ theme.dart # Brand colors, design tokens, Material 3 theme
β βββ alarm_sound_registry.dart # Preset alarm sound definitions
βββ services/
β βββ notification_service.dart # flutter_local_notifications wrapper + MethodChannel
β βββ settings_repository.dart # SharedPreferences-backed AppSettings + PomodoroSettings
β βββ stats_repository.dart # Daily stat persistence + streak tracking
βββ providers/
β βββ focus_provider.dart # ChangeNotifier state machine (idle β running β challengeActive β escalated)
βββ screens/
β βββ home_screen.dart # Idle + running views, today stats card
β βββ challenge_screen.dart # Full-screen math problem + escalation UI
β βββ settings_screen.dart # Interval, difficulty, escalation, alarm toggles
βββ widgets/
βββ aurora_background.dart # Animated mesh-gradient background
βββ brand_mark.dart # Custom-painted target/aperture logo
βββ interval_wheel.dart # Vertical scroll wheel with landmark values
βββ pulse_rings.dart # Animated focus rings
- MethodChannel handler for
id.focusmate.alarm MediaPlayerwithisLooping = truefor the alarm soundVibratorwith repeating patternAudioAttributes.USAGE_ALARMto bypass silent mode
- Same MethodChannel pattern
AVAudioPlayerwithnumberOfLoops = -1(loop forever)
FocusStateenum:idle,running,challengeActive,escalated- State transitions are explicit and gated (e.g. can only escalate from
challengeActive) - All timers cancelled on state transition to prevent leaks
- Download the latest
FocusMate-arm64-vX.Y.Z-release-signed.apkfrom Releases - Transfer to your Android device
- Enable "Install unknown apps" for your file manager / browser (Settings β Apps β Special access β Install unknown apps)
- Open the APK and tap Install
Note on signing: v1.0.0βv1.0.5 used the Android debug key. v1.1.0+ use a proper release keystore. If you have an older version installed, uninstall it first (signatures don't match).
Build from source β see Build from source below. (TestFlight / App Store distribution not yet set up.)
First launch will request:
- Notifications β required for the math challenge alert
- Exact alarms β required for interval-accurate scheduling
- Battery optimization exemption β prevents Android from killing the timer
- Flutter 3.44+ (Dart 3.12+)
- Android SDK 34+ (for Android builds)
- Xcode 15+ (for iOS builds)
- Java 17+ (for Android Gradle builds)
git clone https://github.com/hellogunawan99/focus-mate.git
cd focus-mate
flutter pub getflutter build apk --release --target-platform android-arm64
# Output: build/app/outputs/flutter-apk/app-arm64-v8a-release.apkcd ios && pod install && cd ..
flutter build ios --release
# Then open ios/Runner.xcworkspace in Xcode for device deploymentflutter run # picks first connected device
flutter run -d <device-id> # specific deviceflutter test # all unit + widget tests
flutter test test/math_test.dart # specific file- Unit tests for
MathProblemGenerator(problem difficulty, operator distribution) - Widget tests for
IntervalWheel(centering, scroll range, editor behavior, fling-bound clamps) - All tests run on every commit via widget test suite
$ flutter test
+0: All tests passed!This is a personal-use project, but PRs and bug reports are welcome!
- Check Issues first
- Open a new issue with:
- Device model + Android version
- Focus Mate version (Settings β About)
- Reproduction steps
- Expected vs actual behavior
- Fork the repo
- Create a feature branch (
git checkout -b feature/awesome-thing) - Add tests for new behavior
- Run
flutter analyze+flutter testβ must pass clean - Open a PR with a clear description
flutter analyzeclean- Prefer widget tests for any picker/scroll/offset math
- Document any new design tokens in
lib/core/theme.dart
MIT β see LICENSE for details.
- App icon β procedurally generated target/aperture mark (custom)
- Alarm sound β procedurally generated C5-E5 sine wave (custom)
- Fonts β Plus Jakarta Sans, JetBrains Mono (both open-source)
- Built with β Flutter, Dart,
Material 3, and a lot of
flutter_local_notificationsdebugging
Focus Mate v1.1.3 Β· hellogunawan99 Β· 2026



