Skip to content

cucumberfalse/takeyourmeds

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Take Your Meds

Offline-first medication reminder app (MVP) for iOS and Android. Schedule medications, log intakes (taken/skipped), view history. Built with Flutter. Production targets: iOS and Android (mobile). Web build is for demo/development only (no notifications).


1. Product summary

  • Medications: Add, edit, pause, delete. Daily intake times, repeat interval (e.g. every N days), end condition (never / after X days / after X intakes).
  • Notifications: Local notifications with actions (OK, Skip, Postpone 10/30 min) on mobile only.
  • Journal: History of intakes grouped by date (Today, Yesterday, MMM d, yyyy).
  • Storage: SQLite on mobile; SharedPreferences (localStorage) on web. No sync in MVP.
  • UI: Dark theme, Material 3, go_router, Flutter l10n (ARB). English only; structure ready for more locales.

Key UX decisions:

  • Swipes (Gmail-style): Swipe leftred background and immediate delete, SnackBar "Undo" (7 s). Swipe right — neutral background and pause for 1 day immediately, SnackBar "Undo". Red is used only for delete (destructive). Overflow menu ⋮ — "Pause for X days" and "Delete permanently" (with confirmation).
  • Add flow: Extended FAB "Add", form with Save button at the bottom; time pre-filled with current time for a new medication; focus in the name field when the screen opens. Empty state with CTA "Add first medication".
  • History: Grouping by date (Today, Yesterday, MMM d, yyyy). Delete confirmation via bottom sheet with a red action button.

New team onboarding: Read §2 Architecture, §4 How to run, §5 Tests; then CONTRIBUTING.md and test/README.md for setup, style, and test catalog.


2. Architecture

  • Layers: Domain (entities, no I/O) → Storage (repositories) → App state → UI. UI depends only on IAppState and router.
  • Platform split: Conditional entry (main.dartmain_io.dart / main_web.dart) and conditional export of concrete AppState (IO vs Web). Web does not pull sqflite or native plugins.
  • State: Single IAppState (interface) provided at root via Provider. Implementations: AppState (IO/Web) and test FakeAppState. Screens use context.read<IAppState>() and ListenableBuilder where needed.
  • Routing: go_router only; paths in AppRoutes; no Navigator.pushNamed in app code.

Key paths:

  • lib/domain/ — entities (Medication, IntakeLog) and enums.
  • lib/storage/ — SQLite (database, repositories) and web (SharedPreferences repositories).
  • lib/notification_scheduler/ — mobile notifications and web no-op.
  • lib/app_state_interface.dart — contract for UI and tests; lib/app_state.dart — barrel exporting platform AppState.
  • lib/router/ — go_router config and AppRoutes.
  • lib/screens/ — full screens and bottom sheets.
  • lib/l10n/ — ARB + generated localizations.

3. Tech stack

  • Flutter (SDK ^3.10.8), Provider, go_router, sqflite, path_provider, uuid, flutter_local_notifications, timezone, flutter_timezone, shared_preferences, intl. Dev: flutter_test, integration_test, flutter_lints, mocktail. No deprecated packages.

4. How to run

Prerequisites: Flutter SDK (stable, web enabled) for local run and tests. Docker is optional and runs the same on macOS, Windows, and Linux (Docker Desktop or Engine). For mobile: Xcode (macOS) for iOS; Android SDK for Android.

Using Make (recommended):

Command Description
make deps Install dependencies (flutter pub get)
make run Run on connected device or default target
make run-chrome Run web app in Chrome
make run-chrome-docker Build and run web in Docker → http://localhost:8080
make build-android Build APK
make build-ios Build for iOS
make test Run all unit and widget tests
make test-unit Run unit tests only (domain, storage, router, l10n)
make test-widget Run widget tests only (screens, helpers)
make test-integration Run E2E tests (requires device/emulator)
make gen-l10n Regenerate localizations from ARB
make analyze Run static analysis (lib + test)
make format Format lib and test (run before every commit; CI checks this)
make check Same as CI Lint: analyze + format check
make check-deps Fail if flutter pub get reports "packages have newer versions incompatible with constraints" (same as CI; see test deps_no_outdated_message_test.dart)
make outdated List outdated packages; run periodically to keep deps current (see docs/DEPENDENCIES.md)
make clean Clean build artifacts

Docker (no Flutter required on host): From project root run make run-chrome-docker or cd docker && docker compose up --build. Open http://localhost:8080. The same image runs on macOS, Windows, and Linux.

Without Make: flutter pub get, flutter run, flutter run -d chrome, flutter build apk / flutter build ios, flutter test, flutter analyze lib test, and for E2E: flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart.


5. Tests

  • Unit: test/domain/, test/storage/, test/router/, test/l10n/ — domain models, serialization, route paths, localization strings. Also test/format_check_test.dart (code must be formatted) and test/deps_no_outdated_message_test.dart (no "packages have newer versions" message); if these fail, run make format or fix dependencies (see CONTRIBUTING).
  • Widget: test/screens/, test/helpers/ — screens with FakeAppState (implements IAppState) and Provider.
  • E2E: integration_test/app_test.dart — full app launch; run with make test-integration (or flutter drive ...). Requires device/emulator. E2E is not run in CI; run locally before release or for smoke checks.

A plain-language description of what each test file and test does is in test/README.md (for QA and new developers).

How to add tests:

  • Unit: New file test/<layer>/<name>_test.dart; use test() / expect() from package:flutter_test/flutter_test.dart.
  • Widget: Use testWidgets(), pumpWidget() with MaterialApp and ChangeNotifierProvider<IAppState>.value(value: FakeAppState(), ...), then find.text(), tester.tap(), etc.
  • E2E: Add or edit under integration_test/; ensure IntegrationTestWidgetsFlutterBinding.ensureInitialized() and run the app (e.g. app.main()), then pumpAndSettle() and assert on UI.

6. CI/CD

  • Where it runs: The repo uses GitHub Actions (.github/workflows/ci.yml) on push and PR to main / master. The pipeline is generic (checkout → setup → lint → test → build); the same steps can be run in any CI (GitLab, Jenkins, etc.) using the commands below. Android job requires a runner with Android SDK (e.g. GitHub-hosted ubuntu-latest); the workflow runs flutter doctor --android-licenses before build.
  • Jobs (all must pass):
    1. Lintflutter analyze lib test and dart format --set-exit-if-changed lib test (if any file is unformatted, Lint fails; run make format locally, then make check to verify)
    2. Unit testsflutter test test/domain test/storage test/router test/l10n
    3. Widget testsflutter test test/screens test/helpers
    4. Build Webflutter build web --release (after lint + tests)
    5. Build Android APKflutter build apk --release (after lint + tests)
  • Reproducible: Same commands locally: make analyze, make test-unit, make test-widget, make run-chrome-docker or flutter build apk. No secrets required for CI.
  • Rule: If any job fails, do not merge until fixed.
  • Why Build Android can fail: (1) Old plugins used Flutter V1 embedding (Unresolved reference: Registrar). (2) Some plugins require Android Gradle config (e.g. core library desugaring for flutter_local_notifications); if missing, the build fails. Native/Gradle issues are not caught by unit/widget tests; only the Build Android APK CI job catches them. Before adding or upgrading native plugins, see CONTRIBUTING §9 and docs/DEPENDENCIES.md (including §3 Android native setup).

7. Processes and conventions

SDLC: Feature/bugfix → branch → implement + tests → PR → review → merge to main. Release from main (tag when needed).

Branching: Trunk-based. Main branch is always releasable. Short-lived feature branches; no long-lived release branches. Rationale: single app, small team, MVP; avoids merge hell and keeps deploy path simple.

Code review: Every change via PR. Review checks: lint and tests pass, no unrelated edits, public API and behavior documented where non-obvious.

Definition of Done: Lint passes (flutter analyze lib test); unit/widget tests added/updated for new behavior; README/docs updated if user-facing or process changes; no known regressions.

Test strategy:

  • Unit: Domain and pure helpers (serialization, route constants); fast, no Flutter shell.
  • Widget: Screens and key flows with FakeAppState; cover main UX paths.
  • E2E: Smoke (app launches, main screen visible); optional deeper flows on stable device/emulator.
  • Coverage: Aim for meaningful coverage on domain and repositories; no hard gate in CI yet.

Releases: Version in pubspec.yaml (version: 1.0.0+1). For stores: tag in git, build from tag, upload to App Store Connect / Play Console. No automated store deploy in MVP.


8. Architectural decisions

  • Single global state (IAppState): Keeps MVP simple; no Redux/Bloc. Plan to extract use cases if the app grows.
  • Conditional entry and platform export: Keeps web build free of sqflite and native plugins; tree-shaking and smaller web bundle.
  • IAppState interface: Lets widget tests inject FakeAppState without platform-specific code.
  • go_router: Declarative routes and deep links; single source of truth for paths (AppRoutes).
  • SQLite (mobile) / SharedPreferences (web): No backend in MVP; local-first. Sync and migration strategy deferred.

9. Limitations and known issues

  • Web: No notifications; storage only. Demo/development only.
  • Notifications: Auto-postpone after 10 minutes not implemented (schedule follow-up at T+10 when user does not act; see Backlog). Max 50 scheduled per medication.
  • DB migrations: Schema version 1 only; no migration path yet. Add migrations before changing schema.
  • Web storage: Full lists in SharedPreferences; may hit size limits with large history; no pagination.
  • Accessibility: Contrast and screen-reader support not fully validated.

10. Project structure (short)

lib/
├── main.dart, main_io.dart, main_web.dart   # Entry: main_io (mobile), main_web (web)
├── app_state_interface.dart   # Contract for UI and tests
├── app_state.dart              # Barrel (exports platform AppState)
├── app_state_io.dart, app_state_web.dart
├── domain/                      # medication, intake_log, domain.dart
├── storage/                     # database, repositories, storage_web
├── notification_scheduler/      # mobile + web stub
├── router/                      # app_router.dart, AppRoutes
├── screens/                     # list, edit, history, settings, pause sheet
└── l10n/                        # ARB + generated
test/                            # unit, widget, helpers
integration_test/                # E2E
docker/                          # Dockerfile + compose for web

11. Product Backlog

Prioritization: High = user impact or risk; Medium = clear value, not blocking; Low = nice-to-have or tech debt when capacity allows.

Item Description User value Tech effort Priority
Auto-postpone 10 min After scheduled time, show reminder again in 10 min without logging (schedule follow-up at T+10; cancel if user taps main notification). Fewer missed intakes. Medium (background/plugin constraints). High
DB migrations Version schema and add migration steps before changing tables. Safe updates, no data loss. Low. High
Accessibility pass Semantic labels, touch targets, contrast (WCAG). Usable for screen readers and low vision. Medium. Medium
Settings: theme/language Real theme toggle and language selector (when more locales exist). User control. Medium. Low
Pagination / archive (web) Limit or archive old intake logs in SharedPreferences. Avoid storage limits on web. Medium. Low
Analytics (opt-in) Events for add/edit/pause/delete and intake actions; no PII. Product insights. Medium. Low
Sync (future) Optional cloud backup/sync with auth. Multi-device, backup. High. Low (post-MVP)
Security checklist Document encryption at rest, permissions, data handling. Compliance and trust. Low. Medium

12. References

About

Offline-first mobile medication reminder built with Flutter: local notification actions, flexible schedules, swipeable pause/delete, and a local intake journal.

Topics

Resources

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors