MyNEX Docs / Mobile App
Flutter
Flutter — iOS & Android

"Indian Ocean premium" — the mobile design system

A token-driven Flutter app: deep lagoon blues/teals as the brand identity, silver reserved exclusively for NAQD, dark-mode-first. This page documents the architecture and module map as verified directly in code, not from the design blueprint alone.

Two frontend teams may still be adding screens

This page describes module structure, not an exhaustive screen inventory — treat specific screen lists as a snapshot, and the architecture/patterns as the durable part.

Architecture

lib/features/<feature>/{screens, widgets, providers/services, models} core/theme colors, type, spacing, motion core/widgets 19-widget shared library core/router go_router, static route table core/security biometric, PIN, app-lock, secure storage core/api — ApiClient (Dio) auth/error/logging/cert-pin interceptors Backend API (/api/v1) --dart-define=API_BASE_URL No Solana/Polygon SDK on-device — all chain logic (including NAQD withdrawal) is server-side; the app only posts an address string.
Only WalletProvider is a registered app-wide ChangeNotifier; most other features call their service classes directly from local widget state.

Design system

Palette

Named ramps, quoted directly from lib/core/theme/app_colors.dart. Dark is the canonical scheme — "designed first, light derived from it."

lagoon400
brand
lagoon900
brand dim
abyss950
dark bg
abyss50
light bg
silver300
NAQD only
ocean400
USD identity
success
warning
danger

Per-currency identity is systematic: AppColors.forCurrency() maps MVR → lagoon, USD → ocean, NAQD → silver, everywhere in the app — a balance card, a transaction row, and an icon all agree on which color means which currency. The code comment on the silver ramp is explicit: "using it anywhere else dilutes the signal."

Typography

Inter for UI text, JetBrains Mono for addresses/hashes/reference IDs (both via google_fonts, network-fetched/cached — not bundled assets). Dedicated "money" text styles (moneyDisplay 40px down to moneySmall 13px) apply FontFeature.tabularFigures() and FontFeature.slashedZero() — the code's own reasoning: "so digits never jitter as a balance ticks... 0 and O are never confused in an account number."

Spacing, radius, motion

Token setValues
Spacing (4pt scale)xxs=2 … giant=64, plus a Gap widget replacing bare SizedBox
Radiusxs=4 (badges) → pill=999, scaled so curvature reads consistently regardless of element size
Motion durationsfast=150ms (state flips), base=250ms (default — cards/sheets), slow=400ms (page transitions, balance count-ups), ambient=700ms (shimmer loops only)
Motion curvesstandard=easeOutCubic, plus dedicated enter/exit/move/spring(easeOutBack)/emphasized curves

Shared widget library (lib/core/widgets/)

19 widgets, each solving one duplicated problem the codebase's own comments call out — e.g. app_card.dart replaces hardcoded-white containers that were invisible in dark mode; status_badge.dart replaces four copy-pasted status-color switches; mynex_logo.dart replaces five hand-rolled logo copies with their own hardcoded blue. Notable ones: animated_money.dart (counts up on load), countdown_ring.dart (FX/partner quote expiry), empty_state.dart / error_state.dart (deliberately distinct — "empty means nothing here yet, error means we couldn't find out"), loading_skeleton.dart (content-shaped shimmer, not a spinner), pin_confirm_sheet.dart (bottom-sheet PIN confirmation for money actions).

Router & config

Routing
go_router — a flat List<GoRoute> with static path-constant helpers (e.g. WalletDetail.path(currency)); added specifically because, per the code comment, "thirteen context.push targets had no matching route." A custom errorBuilder shows a designed empty state instead of go_router's raw exception page.
API base URL
String.fromEnvironment('API_BASE_URL') in lib/core/config/app_config.dart. Debug builds fall back to an emulator-aware localhost; release builds throw if unset — no silent fallback to a dev machine ships in production.
Run command
flutter run --dart-define=API_BASE_URL=http://localhost:8181/api
Cert pinning define
Optional --dart-define=API_CERT_SHA256=... activates the pinning interceptor — empty by default

Feature module map

All under lib/features/<feature>/ with a {screens, widgets, services/providers, models} subfolder pattern (not every feature uses every subfolder).

ModuleStatusNotes
splashrealStartup gating — shows a blocked screen for misconfigured API_BASE_URL or a compromised device
authrealLogin, register, PIN setup/unlock, biometric setup — real API-backed
homerealDashboard, consumes WalletProvider, skeleton/error-retry states wired
walletrealConvert, wallet detail — the app's one globally-registered ChangeNotifier lives here
paymentsrealP2P transfer, QR pay/scan/generate, bill pay, request money — covers what the blueprint calls "P2P" and "QR" as one module, not two
naqdrealBuy/sell, operations list/detail, withdraw, silver price chart, reserve panel — async mint/burn with idempotency + polling wired end to end
transactionsrealHistory + detail, reuses wallet's transaction model
profilerealLargest module — KYC submit/documents, 2FA, PIN change, sessions, login history, notification preferences. This is where "settings/security" from the blueprint actually lives; there is no separate settings/ directory
cardsrealList/detail, activate, freeze, limits, card PIN, one-time PAN reveal, request physical — CardService calls the real /v1/cards/* routes, idempotency keys on every write
partnersrealRail listing, quote → execute → operation status for the TnG corridor — a deliberate three-call shape ("the rate is real money and moves") against /v1/partners/*, idempotency key on execute
notificationsrealIn-app notification list, mark-read/read-all against /v1/notifications/*
Fast-moving target

The cards and partners modules were placeholder/absent early in this build and landed as fully real, API-wired features in the course of writing this documentation — a direct illustration of why this page describes structure rather than promising a frozen screen inventory. By this snapshot, every screen category REVAMP_BLUEPRINT.md §4 names checks out against real, API-wired code.

Security services (see also: Security page)

Full detail on Security → Mobile security. In short: biometric auth, 5-minute app auto-lock with a background privacy curtain, secure-storage-backed tokens (with a verified-removed legacy hardcoded-key AES layer), jailbreak/root detection blocking app start, and opt-in certificate pinning are all real, implemented code — not aspirational.

Testing

FileCovers
test_harness.dartShared helper — wraps widgets in the real AppTheme, disables network font fetches for deterministic tests
amount_keypad_test.dartAmountEntry digit entry/display logic
qr_payload_test.dartQR payload encode/decode, matched against the Go server's base64url format
transaction_grouping_test.dartTransaction list date-grouping logic
transfer_review_test.dartWidget test — fee/receipt rows against TransferBreakdown
widget_test.dartApp-level smoke test with a mocked secure-storage channel

No integration_test/ directory and no golden tests — all coverage is unit/widget level. Run with flutter test; static analysis with flutter analyze.