Skip to content

feat: health connect integration (draft)#80

Merged
nuilewis merged 29 commits into
mainfrom
health-connect
Jun 24, 2026
Merged

feat: health connect integration (draft)#80
nuilewis merged 29 commits into
mainfrom
health-connect

Conversation

@nuilewis

Copy link
Copy Markdown
Collaborator

Summary

Adds a first draft implementation of Health Connect (Android) / HealthKit (iOS) integration. It reads three metrics read-only — steps, heart rate, and blood oxygen (SpO2) — and surfaces them on a home dashboard card and a dedicated health detail screen.

This is an early cut: the plumbing, permission flow, and UI are in place, but there's plenty still to do (see below). Merging now to establish the foundation and unblock further work on main.

What's included

  • New health_connect feature following the standard feature-first layout (service → repository → notifiers → screen):
    • HealthConnectService wraps the health package (configure-once, availability check, permission check/request, per-metric reads). Normalizes SpO2 to a 0–100% across Health Connect / HealthKit.
    • HealthRepository returns FutureEither, maps platform booleans onto a HealthPermissionStatus enum, and aggregates reads into a HealthSummary.
    • Two keepAlive notifiers — permission and summary — split by lifecycle.
    • HealthScreen at /health, branching UI on permission state.
  • Home integration: HealthSummaryCard dashboard tile; home screen checks permissions on load and fetches the summary (unawaited) only when granted.
  • Android setup:
    • Health Connect read permissions + permission-rationale handlers in the manifest.
    • MainActivity now extends FlutterFragmentActivity (required for the health plugin's permission launcher) — fixes the "permission launcher not found" / HealthPermissionStatus.denied error.
  • Dependency: adds health: ^12.0.0.

Note: this branch also carries the earlier chore: package upgrade (riverpod pinned to v2) and the resulting regenerated bindings (objectbox, assets, provider .g.dart), plus a small null-guard fix in WaterLogNotifier.calculateTotalFromLogs.

Known limitations / TODO

  • Android-first — iOS/HealthKit path is wired but unverified.
  • Data is in-memory only — no Firebase sync, no ObjectBox persistence.
  • Limited to 3 metrics; no historical/range queries beyond the last 24h.
  • No background refresh, caching, or pull-to-refresh.
  • No tests yet for the service/repository/notifiers.
  • Permission denial UX is minimal (no "open settings" deep link).

Testing

  • Requires the Health Connect app installed and some sample data on-device.
  • Manual: grant permission from the health screen → steps / heart rate / SpO2 populate on the card and detail screen.

🤖 Generated with Claude Code

nuilewis and others added 29 commits June 24, 2026 12:07
…o v2

Downgrade icon_font_generator ^4.0.0 → ^3.2.0 to resolve conflict with
riverpod_generator 2.6.x under Dart 3.12.2 (_macros SDK incompatibility).
All other packages upgraded to latest resolvable versions including Firebase,
ObjectBox 4.3.0, flutter_svg 2.3.0, fpdart 1.2.0, and build tooling.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- add `health: ^12.0.0` for health connect (android) and healthkit (ios) access
- backing dependency for the new health_connect feature

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- regenerate lockfile to resolve `health` and its transitive deps

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add read permissions for steps, heart rate, blood oxygen, activity recognition
- declare androidx permissioncontroller activity for the consent flow
- add rationale intent-filter for android 13 and below
- add viewpermissionusage activity-alias for android 14+
- query the com.google.android.apps.healthdata package so the app can detect/launch health connect

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- switch mainactivity from flutteractivity to flutterfragmentactivity
- the health plugin registers its permission activityresultlauncher in oncreate, which requires a fragmentactivity host
- fixes "permission launcher not found" and the resulting healthpermissionstatus.denied

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `HeartRateRecord` (timestamp + bpm) and `BloodOxygenRecord` (timestamp + spo2 %)
- equatable value types, in-memory only — health connect is the source of truth, no objectbox persistence

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `HealthPermissionStatus` enum (unknown / notavailable / denied / granted)
- add `HealthSummary` aggregating today's steps, latest + recent heart rate and blood oxygen readings
- expose `HealthSummary.empty` as the initial notifier state

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- wrap the `health` package for steps, heart rate, blood oxygen reads
- lazily call `configure()` once before any platform call
- expose availability check, permission check/request, and per-metric fetches
- map raw data points onto feature models and normalize spo2 to a 0–100 percentage across health connect and healthkit
- throw `AppException` on read failures; return false on availability/permission errors

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- define `HealthRepository` contract returning `FutureEither` per app convention
- map raw platform booleans onto `HealthPermissionStatus` (notavailable when unavailable)
- aggregate steps, heart rate, blood oxygen into a single `HealthSummary`
- wrap service calls in `futureHandler` so failures surface as `Failure`

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add keepalive `HealthPermissionNotifier` and `HealthSummaryNotifier`
- split permission and summary state since they have different lifecycles
- wire service + repository singletons, matching the water/meds pattern
- fold either results into asyncvalue data/error with logging

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output for the permission and summary notifiers

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `HealthScreen` at /health showing steps, heart rate and spo2
- fetch the summary on first frame only when permission is granted
- branch ui on permission state (loading / unavailable / denied / granted)
- prompt the permission request and re-fetch on grant

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- re-export models, service, repository, notifiers and screen for single-import access

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `HealthSummaryCard` dashboard tile beside the water card
- watch the health providers directly and adapt to permission state
- tap navigates to the health detail screen

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `health_summary_card.dart` to the components barrel

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- check health permissions on load, then fetch the summary only if granted
- fetch unawaited so it does not delay the rest of the dashboard
- render `HealthSummaryCard` under the health info heading, tapping through to /health

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `HealthScreen` autoroute at /health
- import the health_connect barrel into the router

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- autoroute output for the new /health route

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- return 0.0 from `calculateTotalFromLogs` when `state.value` is null
- avoids a null-assertion crash before logs have loaded

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the riverpod v2 pin

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the riverpod v2 pin

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the riverpod v2 pin

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the riverpod v2 pin

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the riverpod v2 pin

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the riverpod v2 pin

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- build_runner output after the package upgrade

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- flutter_gen output after the package upgrade

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- add `android.builtInKotlin=false` and `android.newDsl=false` added by the flutter migrator

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@nuilewis
nuilewis marked this pull request as ready for review June 24, 2026 17:03
@nuilewis
nuilewis merged commit 37f5220 into main Jun 24, 2026
3 of 4 checks passed
@nuilewis
nuilewis deleted the health-connect branch June 24, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant