# M-TEER Mobile App

Flutter client for the M-TEER learning platform. **No login** — learners are
identified by an anonymous device session.

The app contains no educational content. Sections, modules, activities and
content blocks are fetched from the API and rendered generically, so new
material published from the admin panel appears without an app release.

## Prerequisites

| | |
| --- | --- |
| Flutter SDK | 3.0 or later — https://docs.flutter.dev/get-started/install |
| Android Studio | only if you want to run on Android (provides the SDK + emulator) |
| Chrome | enough on its own to run the web build |

Verify with `flutter doctor`.

## Setup

Three files are machine-specific, gitignored, and must be generated locally:
`android/local.properties` (path to *your* Flutter SDK), `android/gradlew`, and
the Gradle wrapper jar. Run:

```powershell
cd mobile
powershell -ExecutionPolicy Bypass -File tool/setup.ps1
```

or do it by hand:

```bash
cd mobile
flutter create --project-name m_teer_mobile --org com.mteer --platforms=android,web .
flutter pub get
```

`flutter create` only adds missing platform scaffolding — `lib/`, `test/` and
`pubspec.yaml` are left alone. It does rewrite the debug manifest, so re-check
that `android/app/src/debug/AndroidManifest.xml` still references
`@xml/network_security_config` (the setup script restores this automatically).

## Running

Start the API first (`cd backend-api && npm run start:dev`), then:

```bash
# Android emulator — 10.0.2.2 is the host machine as seen from the emulator
flutter run --dart-define=API_BASE_URL=http://10.0.2.2:3000

# Physical Android device over USB
adb reverse tcp:3000 tcp:3000
flutter run --dart-define=API_BASE_URL=http://localhost:3000

# Chrome — no Android SDK needed
flutter run -d chrome --dart-define=API_BASE_URL=http://localhost:3000
```

`API_BASE_URL` defaults to `http://10.0.2.2:3000` if omitted.

## Architecture

```
lib/
  core/
    config/     API base URL, media URL resolution
    network/    HTTP client that unwraps the { success, data } envelope
    router/     go_router routes (no login route by design)
    storage/    preferences for scalars, JSON files for cached content
    theme/      Material 3 theme
  models/       API payload models + Stroke (vector drawing data)
  services/     session, content (+offline cache), progress, drawing
  providers/    ChangeNotifier state for content and progress
  screens/      splash, home, section, module, activity, progress, my work,
                favorites, quick reference, search, settings
  widgets/
    blocks/     BlockRenderer — maps a block `type` to a widget
    drawing/    vector drawing engine (undo/redo/eraser/zoom/templates)
    quiz/       renders questions, submits for server-side grading
    decision_tree/  walks an admin-authored node/edge graph
```

**The renderer is the core.** `BlockRenderer` switches on `block.type`. A type
this build does not know renders a readable placeholder rather than crashing, so
an older install keeps working when new block types are introduced.

**Offline.** The catalogue and each module tree are cached to disk. Progress,
drawings and reflections are written locally first and pushed to the API
opportunistically; `/public/manifest` lets a sync download only what changed.

**Quiz answers** are never in the payload the device receives. Submissions are
graded by the API.

## Testing

```bash
flutter analyze
flutter test
```

Two SDK-free checks are also available, useful when Flutter is not installed:

```bash
python tool/check_imports.py   # every relative/in-app import resolves
python tool/check_balance.py   # braces, parens and brackets balance
```

## Troubleshooting

**"CLEARTEXT communication not permitted"** — Android 9+ blocks plain HTTP. The
debug build whitelists `10.0.2.2`, `10.0.3.2`, `localhost` and `127.0.0.1` via
`android/app/src/main/res/xml/network_security_config.xml`. If you point the app
at a LAN address, add that host there.

**Empty home screen** — the app only shows `PUBLISHED` content. Check
`curl http://localhost:3000/api/v1/public/sections` returns data, and that the
API is reachable from the device (emulator uses `10.0.2.2`, not `localhost`).

**"flutter.sdk not set in local.properties"** — run the setup step above.
