# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project CoinVision — a production cryptocurrency exchange (CEX) Flutter app targeting Android and iOS. ## Commands ```bash # Run flutter run # Build flutter build apk --debug --target-platform android-arm64 # Android debug flutter build apk --release --target-platform android-arm64 # Android release flutter build ipa --release --export-method=ad-hoc # iOS ad-hoc flutter build ipa --release # iOS release # Code generation (required after model/provider changes) dart run build_runner build --delete-conflicting-outputs dart run build_runner watch # watch mode # Format & analyze dart format . flutter analyze # Test flutter test # Reset flutter clean && flutter pub get ``` ### Runtime overrides via `--dart-define` ```bash flutter run --dart-define=API_BASE_URL=https://custom.api.com/api/ flutter run --dart-define=WS_URL=wss://custom.ws.com/market flutter run --dart-define=ENABLE_MOCK=true ``` ## Architecture Strict 3-layer clean architecture. **Never bypass layers.** ``` 用户交互 → Notifier → Repository → Service/API ↓ UI (Widget) ←── State (immutable) ←── Notifier ←── ``` | Layer | Location | Responsibility | |-------|----------|----------------| | **Presentation** | `lib/presentation/` | Rendering & user interaction only. No business logic. | | **Provider (ViewModel)** | `lib/providers/` | Riverpod Notifiers. Expose immutable State + methods. | | **Data** | `lib/data/` | Services (stateless HTTP), Repositories (SSOT, caching), Models (Freezed) | | **Core** | `lib/core/` | Dio client, GoRouter, theme, l10n, utils. No business logic. | ### State Management — Riverpod 2 - Use `@riverpod` annotation for code generation - State objects must be **immutable Freezed classes**, updated with `copyWith` - In Widgets: `ref.watch()` for state, `ref.read(…notifier)` for actions - Notifier re-initialization: `ref.invalidate(someProvider)` ### Navigation — GoRouter 14 - All routes defined in `lib/core/router/app_router.dart` - StatefulShellRoute with 5 bottom-nav branches (preserves tab state) - Auth guard: routes under `/asset`, `/user/*`, `/broker/*` require login - Session expiry: API error code `4000` triggers logout dialog - **Always use `context.push()` / `context.go()`** — never `Navigator.push` ### Networking — Dio - Central client in `lib/core/network/dio_client.dart` with interceptors for token injection, error wrapping, and node failover - Multi-node resilience: `lib/providers/node_provider.dart` — speed tests nodes, circuit-breaks after 3 failures for 60s - Timeouts: connect 15s, receive 15s - Session error (code 4000) → auto logout; interceptor suppresses duplicate navigation ### Data Models — Freezed - All domain models use `@freezed` with `fromJson`/`toJson` - Run `build_runner` after any model or provider annotation change - Generated files (`*.freezed.dart`, `*.g.dart`) are committed ### Storage | Data | Storage | |------|---------| | Auth tokens | `flutter_secure_storage` (platform vault) | | User preferences | `SharedPreferences` | | Local caching | Hive | ### Real-time Data — WebSocket - `web_socket_channel` for market data streams - Lifecycle managed in `lib/providers/ws_provider.dart` - Manual reconnect logic on disconnect ## Key Conventions ### New feature development order (required) 1. Define Freezed domain model (`data/models/`) 2. Implement Service — stateless HTTP only (`data/services/`) 3. Implement Repository — SSOT, caching, data transform (`data/repositories/`) 4. Implement Notifier — Freezed State + methods (`providers/`) 5. Implement Screen/Widget — ConsumerWidget, 3-state UI (`presentation/`) ### UI rules - Widgets use `ConsumerWidget` or `ConsumerStatefulWidget` - Always handle **loading / error / empty** three states - Use `AppColors` — rise: `#0ecb81`, fall: `#f6465d` - Use `formatPrice()` / `formatChange()` from `core/utils/` — never hardcode decimal places - Layout scaled with `flutter_screenutil` (design base: 375×812) - Use `ListView.builder` for lists; `const` constructors for static sub-widgets - BottomSheets via `showModalBottomSheet` with `isScrollControlled: true` ### Localization 5 locales: `zh`, `zh_TW`, `en`, `ja`, `ko`. ARB files in `lib/core/l10n/`. Config in `l10n.yaml`. ### Environment / Build - Debug config: `lib/core/config/env_debug.dart`, Android package `com.ibit.app.test` - Release config: `lib/core/config/env_release.dart`, Android package `com.ibit.app` - Android signing: keystore at `android/app/keystore`, password in `android/key.properties` (gitignored) - Version format in `pubspec.yaml`: `major.minor.patch+major*10000+minor*100+patch` ## Git Commit Convention `(): ` (Conventional Commits) Scopes: `auth`, `market`, `trading`, `futures`, `asset`, `user`, `core`, `widget`, `l10n` Use `/commit` skill to generate commit messages. Only commit locally — do not push unless explicitly asked. ## Pre-commit Checklist - `dart format .` — no errors - `flutter analyze` — no errors or warnings - No `print()` / `debugPrint()` left in code - `build_runner` output is up to date - State is immutable Freezed; updated via `copyWith` - Tokens stored in `flutter_secure_storage`, not `SharedPreferences`