|
|
@@ -0,0 +1,138 @@
|
|
|
+# 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
|
|
|
+
|
|
|
+`<type>(<scope>): <subject>` (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`
|