Flutter app for personal health data: MedGemma runs on the phone, FHIR comes in over MCP, and Apple Health adds day-to-day readings next to clinic data.
We started with small Gemma-style models for local inference. The app now targets MedGemma (a clinical Gemma variant) so inference can stay on the device.
Privacy. Prompts, FHIR JSON, and Apple Health-backed rows can stay on the phone when you use the full local path. You do not need a cloud LLM for that mode.
Why local on a phone. Phones sit idle a lot of the time. The app only does heavy work when you use it, so it can use spare CPU/GPU without paying for a server per user.
The phone stores EHR-style FHIR (from your MCP server) and Apple Health metrics in SQLite so queries and RAG can use both clinic records and home or wearable data.
Apple Health (iOS). The app reads HealthKit metrics (glucose, heart rate, steps, blood pressure) into SQLite. It also pulls Clinical Health Records FHIR payloads for institutional lab results (e.g. Sonora Quest, Quest Diagnostics hospitals on Apple Health Accounts) via a native iOS helper that queries HKClinicalRecord labResultRecord samples—not only the Dart health package. Labs are stored in health_lab_results (LOINC, value, units, reference range, source, draw time).
Labs in the UI. Health → Lab results shows the latest value per analyte (grouped by LOINC when present). Tap a row for a clinician-style history table (up to twelve prior draws): date/time, result, reference range, in-range flag, Δ vs prior reading, source. Connect Apple Health under Profile, run Sync now, and approve Clinical Records lab access when iOS prompts.
iOS Info.plist. In addition to NSHealthShareUsageDescription / NSHealthUpdateUsageDescription, NSHealthClinicalHealthRecordsShareUsageDescription must be present. HealthKit terminates the process if you request HKClinicalType lab-authorization without that key—it is wired in ios/Runner/Info.plist. Entitlements include health-records; native parsing lives in ios/Runner/ClinicalHealthRecordsPlugin.swift, bridged via lib/services/clinical_lab_results_channel.dart.
See docs/SQLITE_SCHEMA.md and docs/INTEGRATED_HEALTH_EHR_DESIGN.md for tables and how EHR and Apple data fit together.
MCP and FHIR. The app includes an MCP (Model Context Protocol) client that talks to an MCP FHIR server built for this project. The server exposes FHIR resources (Patient, Observation, bundles, etc.) as MCP tools. The client pulls data, stores it in SQLite (fhir_patients, fhir_resources, fetch_summaries), and feeds it into query and RAG code.
Two paths into SQLite.
- FHIR from the server: MCP sync → SQLite.
- Apple Health: HealthKit → app service → SQLite (
health_*tables, keyed by app user id).
docs/INTEGRATED_HEALTH_EHR_DESIGN.md explains how we merge or tag EHR JSON and Apple-derived facts so the model can tell chart data from device data.
-
Weights. MedGemma 4B is shipped as a GGUF file. The download URL is in
AppConfig.gemmaModelUrlinsidelib/config/app_config.dart. The file is large and is not stored in git. -
Download. On iOS and Android the app starts
background_downloaderat launch so the GGUF can download in the background with sensible timeouts. Seelib/main.dartandlib/services/gemma_model_service.dart. -
Load and run.
llamadartloads the GGUF into aLlamaEngine. We cap context size, GPU layers, batch sizes, and output length so phones do not run out of memory. Details:docs/MEDGEMMA_AND_MEMORY_CAPS.md. -
Queries.
GemmaModelServicehandles download and load.GemmaRAGServiceandLocalQueryServicepull text from SQLite (FHIR plus optional merged Apple Health) for retrieval-augmented prompts. -
Home screen. You type or dictate questions;
QueryProviderties requests to the current user (appUserId) and FHIR patient id where applicable.
┌──────────────────┐ ┌─────────────────────┐
│ MCP FHIR server │ │ Apple HealthKit │
└────────┬─────────┘ └──────────┬──────────┘
│ MCP client │ Health sync
▼ ▼
┌─────────────────────────────────────────────────┐
│ SQLite: users, fhir_*, health_* (incl. health_lab_results labs) │
└────────────────────────┬────────────────────────┘
│ LocalQueryService / RAG
▼
┌─────────────────────────────────────────────────┐
│ MedGemma (llamadart) on the phone │
└─────────────────────────────────────────────────┘
Test database and Dify. To copy this SQLite onto a simulator or phone, or to export CSV/Markdown for Dify, read docs/DATABASE_FIXTURE_TESTING_AND_DIFY.md.
| Topic | Paths |
|---|---|
| MCP client, routing, state | lib/main.dart, lib/services/mcp_client.dart, lib/providers/ |
| MedGemma, download, memory limits | lib/services/gemma_model_service.dart, docs/MEDGEMMA_AND_MEMORY_CAPS.md |
| Apple Health → SQLite (vitals + clinical lab FHIR) | lib/services/apple_health_service.dart, ios/Runner/ClinicalHealthRecordsPlugin.swift, lib/services/clinical_lab_results_channel.dart, lib/screens/health_lab_results_screen.dart, lib/screens/health_lab_detail_screen.dart, lib/services/database_service.dart |
| EHR + Apple design | docs/INTEGRATED_HEALTH_EHR_DESIGN.md |
| Server URL and model URL | lib/config/app_config.dart |
You need Flutter 3.8+, Dart 3.8+, Xcode for iOS, and the Android SDK for Android.
git clone https://github.com/maheshbalan/myWellWallet.git
cd myWellWallet
flutter pub get
dart run build_runner build --delete-conflicting-outputs
flutter pub run flutter_launcher_icons
flutter runIn-place installs (preserve data). Updating the same app on the phone replaces only the application bundle—not the app’s Documents / Library sandbox—so your SQLite database, registered users row, downloaded MedGemma weights, and other local files stay intact, as long as you do not delete the app icon first and the bundle identifier stays com.mywellwallet.mywellwallet.
- Recommended:
flutter run --release -d <device_id>afterflutter devices(or Xcode → openios/Runner.xcworkspace→ select your iPhone → Run). This installs a signed build over the existing app. Append--no-residentif you only want install/launch without keeping the tooling attached (flutter run --release -d <device_id> --no-resident). - Avoid
flutter installfor upgrades if you see it log “Uninstalling old version…”—that step can remove local data. If you only have the CLI, useflutter run --releaseinstead offlutter install.
Physical iPhone. Use flutter run --release when running with the native ML stack; see docs/IOS_DEBUG_CRASH.md if debug builds crash.
Apple Health. See docs/APPLE_HEALTH_SETUP.md (HealthKit, Profile → Apple Health, sync interval). Add participating providers under Apple Health → Browse → Add Account so Clinical lab records can sync before you open Lab results.
MCP base URL. Edit mcpBaseUrl in lib/config/app_config.dart.
- MCP traffic should use TLS.
- If MedGemma runs fully on-device, that path does not require a cloud LLM.
- FHIR and Apple Health rows live in SQLite on the device first.
- The app is a client of your MCP FHIR server; access rules live on the server side.
We want the wallet to join a federated learning setup called FLAI, developed with Eli Yune at Balkeum Labs. Devices would send updates such as gradients or summaries instead of sending raw PHI, which matches the on-device privacy goal.
Presented at ICCE 2026 (44th IEEE International Conference on Consumer Electronics), Dubai, February 2026. Conference site.
Paper (PDF): FLAI, IEEE ICCE authored copy (Dropbox)
We are also looking at federated LoRA (low-rank adapters) for models like MedGemma. The full weights stay fixed; small adapter matrices are trained or combined across participants. That cuts upload size versus full-model federated learning and still fits a local GGUF plus adapter layout on the phone.
A paper on this topic has been submitted to BCCA, Barcelona, November 2026.
Planned work includes a harness around the local model:
- A knowledge graph for diseases, drugs, guideline snippets, and patient facts, used for lookup next to the neural model.
- A rules layer that checks model output against stored protocols (paths, contraindications, numeric bands where we encode them). This is for research demos only; it is not medical advice.
- Per-condition configs (for example diabetes, heart disease, cancer) that turn on different graph slices, rules, and RAG sources so one app build can focus the harness without splitting the whole codebase.
The idea is to pair neural output with explicit clinical structure on top of the current SQLite + FHIR + Apple Health stack.
A separate class project (team: Mahesh Balan, Yashas Basavaraj Mahesh, Prajwal Vinod Naik, Brandon Medina) compares several on-device models (Gemma 3, Phi-3, MedGemma tiers) using Dify as the evaluation harness, focused on Type 2 diabetes scenarios and synthetic FHIR + RWE data. It does not replace the main app; it lives under:
projects/local-llm-evaluation/ — proposal summary, Dify checklists, eval test-set layout, model matrix, and results layout.
Pull requests welcome. Please run flutter analyze before you submit.
- License: MIT
- Author: Mahesh Balan — @maheshbalan
Thanks to the Flutter and Dart teams, the FHIR community, the llamadart project and open medical model publishers, and co-authors and partners on federated learning and mobile health work.