Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
12dc2f4
init claude
nuilewis Jun 24, 2026
0226498
chore: upgrade packages to latest compatible versions, pin riverpod t…
nuilewis Jun 24, 2026
43d7134
chore: add health package dependency
nuilewis Jun 24, 2026
8a3bba5
chore: lock health package dependencies
nuilewis Jun 24, 2026
ee9c0cb
feat: declare health connect permissions and rationale handlers
nuilewis Jun 24, 2026
9835793
fix: extend flutterfragmentactivity for health permission launcher
nuilewis Jun 24, 2026
7db29c2
feat: add health record models
nuilewis Jun 24, 2026
ba40884
feat: add health summary model and permission status enum
nuilewis Jun 24, 2026
115536a
feat: add health connect service
nuilewis Jun 24, 2026
2dd3731
feat: add health repository
nuilewis Jun 24, 2026
2ca4e32
feat: add health connect notifiers
nuilewis Jun 24, 2026
c44bd6f
chore: generate health connect notifier bindings
nuilewis Jun 24, 2026
b9c7707
feat: add health detail screen
nuilewis Jun 24, 2026
5a95b79
feat: add health_connect barrel export
nuilewis Jun 24, 2026
741afab
feat: add health summary card to home
nuilewis Jun 24, 2026
8db8ab0
feat: export health summary card from home components
nuilewis Jun 24, 2026
b2ae419
feat: wire health connect into home screen
nuilewis Jun 24, 2026
e16545e
feat: register health screen route
nuilewis Jun 24, 2026
899085e
chore: generate health screen route
nuilewis Jun 24, 2026
4b96155
fix: guard null state in water log total calculation
nuilewis Jun 24, 2026
efb3ce7
chore: regenerate auth notifier bindings
nuilewis Jun 24, 2026
6c276b7
chore: regenerate user notifier bindings
nuilewis Jun 24, 2026
10279d8
chore: regenerate med notifier bindings
nuilewis Jun 24, 2026
c08bff1
chore: regenerate med scheduled doses notifier bindings
nuilewis Jun 24, 2026
c67c302
chore: regenerate water log notifier bindings
nuilewis Jun 24, 2026
f24da38
chore: regenerate water prefs notifier bindings
nuilewis Jun 24, 2026
a45ac64
chore: regenerate objectbox bindings
nuilewis Jun 24, 2026
062aa7a
chore: regenerate asset bindings
nuilewis Jun 24, 2026
58a4d29
chore: apply flutter gradle migrator flags
nuilewis Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Circle is a Flutter app for managing Sickle Cell disease — medication tracking, water intake, vitals, and emergency contacts. Backend is Firebase (Auth, Firestore, Crashlytics, Analytics); local persistence is ObjectBox.

## Commands

```bash
# Dependencies
flutter pub get

# Code generation (required after any changes to providers, routes, or ObjectBox entities)
flutter pub run build_runner build --delete-conflicting-outputs
# Watch mode during development
flutter pub run build_runner watch --delete-conflicting-outputs

# Lint and static analysis
flutter analyze
dart fix --apply

# Tests
flutter test
flutter test test/path/to/specific_test.dart # single test file

# Run app
flutter run

# Build
flutter build apk --debug
flutter build apk --release
flutter build appbundle --release
```

**Do not manually edit the version build number** (`pubspec.yaml` `+N`). CI/CD increments it automatically on release branches.

## Architecture

Feature-first layout under `lib/features/` with six features: `auth`, `meds`, `water`, `home`, `emergency`, `profile`. Each feature follows the same internal structure:

```
features/<name>/
models/ # Plain Dart/ObjectBox entity classes
services/ # Firebase + ObjectBox I/O (no business logic)
repositories/ # Abstract interfaces + implementations; wrap services, convert exceptions to Failure
providers/ # Riverpod notifiers; inject repositories, manage state
screens/ # UI only — reads provider state, dispatches actions
```

Shared code lives in `lib/core/` (routing, theme, error types, extensions, utils) and `lib/components/` (reusable widgets).

### Data Flow

```
Screen → Notifier (Riverpod) → Repository → Service (Firebase/ObjectBox)
```

### Key Patterns

**Functional error handling** — all async operations return `FutureEither<T>` (`Future<Either<Failure, T>>`), defined in `core/utils/typedefs.dart`. Use **fpdart** `Either`/`TaskEither` throughout; avoid bare try/catch in repositories.

**Riverpod + code gen** — providers use `@Riverpod` annotation; generated base classes live in `*.g.dart` files. Notifiers extend `_$ClassName`. Run `build_runner` whenever annotations change.

**AutoRoute** — routes are declared in `lib/core/routes/routes.dart` and generated into `routes.gr.dart`. Add new routes there and regenerate.

**ObjectBox** — entities annotated with `@Entity()`. The store singleton is initialized in `lib/databse_service.dart` (note the typo in the filename). ObjectBox Admin runs on port 8091 in debug mode for inspecting the local DB.

### State Management Details

Notifiers receive repositories through their `build()` method (constructor injection via Riverpod). The pattern ensures testability — pass mock repositories in tests.

### Theme

Material 3. Colors, text styles, and spacing constants live in `lib/core/theme/`. Font is Plus Jakarta Sans. Don't hardcode colors or spacing — use `AppColors`, `AppTextStyles`, and `AppConstants`.

## Code Generation Files

These are generated — do not edit by hand:
- `lib/objectbox.g.dart`, `lib/objectbox-model.json`
- `lib/core/routes/routes.gr.dart`
- `lib/gen/assets.gen.dart`, `lib/gen/fonts.gen.dart`
- Any `*.g.dart` file alongside a provider/entity

## CI/CD

- PRs to `main`: runs `flutter analyze` + builds debug APK
- PRs from `release/*` to `main`: auto-increments version, builds release artifacts, deploys to Play Store internal track
- Create a `release/x.y.z` branch and PR to `main` to cut a release
29 changes: 29 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,22 @@

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

<!-- Health Connect read permissions -->
<uses-permission android:name="android.permission.health.READ_STEPS"/>
<uses-permission android:name="android.permission.health.READ_HEART_RATE"/>
<uses-permission android:name="android.permission.health.READ_BLOOD_OXYGEN"/>
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>

<application
android:label="Circle"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<!-- Health Connect permission rationale handler -->
<activity
android:name="androidx.health.connect.client.PermissionController"
android:exported="false"/>

<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ActionBroadcastReceiver" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
<receiver android:exported="false" android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
Expand Down Expand Up @@ -43,7 +55,22 @@
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
<!-- Health Connect permission rationale (Android 13 and below) -->
<intent-filter>
<action android:name="androidx.health.ACTION_SHOW_PERMISSIONS_RATIONALE" />
</intent-filter>
</activity>
<!-- Health Connect permission rationale (Android 14+) -->
<activity-alias
android:name="ViewPermissionUsageActivity"
android:exported="true"
android:targetActivity=".MainActivity"
android:permission="android.permission.START_VIEW_PERMISSION_USAGE">
<intent-filter>
<action android:name="android.intent.action.VIEW_PERMISSION_USAGE" />
<category android:name="android.intent.category.HEALTH_PERMISSIONS" />
</intent-filter>
</activity-alias>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
Expand All @@ -60,5 +87,7 @@
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
<!-- Allow the app to detect and launch the Health Connect app -->
<package android:name="com.google.android.apps.healthdata"/>
</queries>
</manifest>
4 changes: 2 additions & 2 deletions android/app/src/main/kotlin/com/circleapp/app/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package co.circleapp.app

import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterFragmentActivity

class MainActivity: FlutterActivity()
class MainActivity: FlutterFragmentActivity()
4 changes: 4 additions & 0 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
# This builtInKotlin flag was added automatically by Flutter migrator
android.builtInKotlin=false
# This newDsl flag was added automatically by Flutter migrator
android.newDsl=false
4 changes: 4 additions & 0 deletions lib/core/routes/routes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'package:auto_route/auto_route.dart';
import '../../components/bottom_nav_bar.dart';
import '../../features/auth/auth.dart';
import '../../features/emergency/emergency.dart';
import '../../features/health_connect/health_connect.dart';
import '../../features/home/home.dart';
import '../../features/meds/meds.dart';
import '../../features/profile/profile.dart';
Expand Down Expand Up @@ -30,6 +31,9 @@ class AppRouter extends RootStackRouter {
),
AutoRoute(page: route.WaterEmptyScreen.page, path: WaterEmptyScreen.path),

// ////-------Health Connect-------//// //
AutoRoute(page: route.HealthScreen.page, path: HealthScreen.path),

// ////-------Emergency-------//// //
AutoRoute(page: route.EmergencyScreen.page, path: EmergencyScreen.path),
AutoRoute(
Expand Down
Loading
Loading