refactor(apps): 主题系统迁移至 ColorScheme + 扩展架构并支持 Dark Mode
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
# App Theme Dark Mode and Hardcoded Color Migration Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Enable automatic light/dark mode following system settings and gradually migrate UI color usage from direct `AppColors` references to theme-driven semantic colors.
|
||||
|
||||
**Architecture:** Introduce a dual-theme foundation in `AppTheme` (`light` and `dark`) and route app root to `ThemeMode.system`. Then migrate color access in batches: first shared widgets, then high-traffic feature pages, then long-tail modules. Keep `AppColors` as design-token source only inside theme/token layers during migration.
|
||||
|
||||
**Tech Stack:** Flutter Material 3 (`ThemeData`, `ColorScheme`, `ThemeMode`), existing design tokens (`AppColors`, `AppSpacing`, `AppRadius`), flutter analyze/test verification.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Theme Foundation and System Dark Mode Wiring
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/core/theme/app_theme.dart`
|
||||
- Modify: `apps/lib/app/app.dart`
|
||||
|
||||
**Step 1: Build dual-theme entry points**
|
||||
|
||||
- Add `AppTheme.dark` and a shared private builder `AppTheme._buildTheme(Brightness)`.
|
||||
- Ensure light and dark themes both use Material 3 and `ColorScheme.fromSeed`.
|
||||
|
||||
**Step 2: Remove theme-level hardcoded semantic colors**
|
||||
|
||||
- Replace direct `AppColors` usage in app bar/button/input decoration theme with color-scheme-driven values.
|
||||
- Keep radii and spacing tokens unchanged.
|
||||
|
||||
**Step 3: Wire root app to system mode**
|
||||
|
||||
- In `MaterialApp.router`, set `theme`, `darkTheme`, and `themeMode: ThemeMode.system`.
|
||||
|
||||
**Step 4: Verify**
|
||||
|
||||
Run: `cd apps && flutter analyze`
|
||||
Expected: no new analyzer errors.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Shared Widget Batch (P0) - Replace Direct AppColors with Theme Semantics
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/shared/widgets/error_retry_surface.dart`
|
||||
- Modify: `apps/lib/shared/widgets/confirm_sheet.dart`
|
||||
- Modify: `apps/lib/shared/widgets/destructive_action_sheet.dart`
|
||||
- Modify: `apps/lib/shared/widgets/toast/toast_type_config.dart`
|
||||
|
||||
**Step 1: Migrate simple error surface**
|
||||
|
||||
- Use `Theme.of(context).colorScheme.error` for retry message color.
|
||||
|
||||
**Step 2: Migrate confirm/destructive sheets**
|
||||
|
||||
- Replace sheet background/border/text/primary-action color bindings with `ColorScheme` semantic roles (`surface`, `outlineVariant`, `onSurface`, `onSurfaceVariant`, `primary`, `onPrimary`, `error`, `onError`).
|
||||
|
||||
**Step 3: Migrate toast style mapping**
|
||||
|
||||
- Replace static feedback colors with semantic color-scheme mappings per toast type.
|
||||
- Preserve existing icon and l10n label behavior.
|
||||
|
||||
**Step 4: Verify**
|
||||
|
||||
Run: `cd apps && flutter analyze`
|
||||
Expected: no new analyzer errors.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Batch Migration Governance and Rollout Rules
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/bugs/AppTheme硬编码颜色且缺失DarkMode.md`
|
||||
|
||||
**Step 1: Document batch strategy**
|
||||
|
||||
- Add phased rollout strategy: Foundation -> Shared widgets (P0) -> Core pages (P1) -> Long tail (P2) -> guardrails.
|
||||
|
||||
**Step 2: Define acceptance and rollback criteria**
|
||||
|
||||
- Include per-batch verification (`flutter analyze`, targeted widget tests/manual dark mode checks).
|
||||
- Require small-scope commits per batch for safe rollback.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: P1/P2 Execution Backlog (Follow-up)
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/home/**`
|
||||
- Modify: `apps/lib/features/settings/**`
|
||||
- Modify: `apps/lib/features/auth/**`
|
||||
- Modify: remaining `apps/lib/features/**`
|
||||
|
||||
**Step 1: Prioritize by user path**
|
||||
|
||||
- P1 order: home -> settings -> auth.
|
||||
- P2: remaining feature pages and low-frequency screens.
|
||||
|
||||
**Step 2: Migrate per file with semantic mapping**
|
||||
|
||||
- Replace direct `AppColors` usage in widget code with `Theme.of(context).colorScheme` or scoped theme helpers.
|
||||
- Keep visual behavior equivalent first; optimize polish after semantic migration is stable.
|
||||
|
||||
**Step 3: Verify per batch**
|
||||
|
||||
Run:
|
||||
- `cd apps && flutter analyze`
|
||||
- `cd apps && flutter test` (targeted where tests exist)
|
||||
|
||||
Expected: no regressions in targeted flows and theme switching.
|
||||
|
||||
---
|
||||
|
||||
## Risks and Mitigations
|
||||
|
||||
- Risk: dark mode contrast regression in legacy widgets.
|
||||
- Mitigation: migrate shared widgets first, then high-traffic pages with screenshot/manual checks.
|
||||
- Risk: mixed semantic + static colors during transition.
|
||||
- Mitigation: enforce migration order and avoid adding new direct `AppColors` in UI layers.
|
||||
- Risk: broad-scope change fatigue.
|
||||
- Mitigation: ship in small batches with isolated verification and rollback points.
|
||||
|
||||
## Done Criteria
|
||||
|
||||
- `MaterialApp` follows `ThemeMode.system` with `theme` and `darkTheme` configured.
|
||||
- Theme layer no longer relies on static semantic colors for app-wide component themes.
|
||||
- P0 shared widget batch migrated to theme semantics.
|
||||
- Migration plan and execution progress documented in bug tracker doc.
|
||||
@@ -0,0 +1,144 @@
|
||||
# Data Repositories Cache Strategy Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Establish a shared cache abstraction and shared data-repository entrypoints so cross-feature data access no longer depends on direct feature-to-feature data imports.
|
||||
|
||||
**Architecture:** Keep cache infrastructure centralized in `apps/lib/data/cache/`, and expose cross-feature data through `apps/lib/data/repositories/` facades registered in DI as singletons. Feature screens consume repositories, while cache policy/key/invalidation remain in repository layer.
|
||||
|
||||
**Tech Stack:** Flutter, Dart, GetIt, flutter_test
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Finalize Shared Cache Foundation
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/data/cache/cached_repository.dart`
|
||||
- Modify: `apps/lib/features/settings/data/services/user_profile_cache_repository.dart`
|
||||
- Test: `apps/test/features/settings/data/services/user_profile_cache_repository_test.dart`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add an invalidate-vs-inflight regression case in `user_profile_cache_repository_test.dart` asserting stale in-flight refresh cannot restore `cachedUser` after invalidate.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `flutter test test/features/settings/data/services/user_profile_cache_repository_test.dart`
|
||||
|
||||
Expected: FAIL before generation-guard fix.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Add generation/version guard in `UserProfileCacheRepository` so `invalidate()` increments generation and stale async loader results are ignored for in-memory snapshot updates.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `flutter test test/features/settings/data/services/user_profile_cache_repository_test.dart`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/lib/data/cache/cached_repository.dart apps/lib/features/settings/data/services/user_profile_cache_repository.dart apps/test/features/settings/data/services/user_profile_cache_repository_test.dart
|
||||
git commit -m "refactor: harden user profile cache invalidation race handling"
|
||||
```
|
||||
|
||||
### Task 2: Introduce Shared Data Repositories Module
|
||||
|
||||
**Files:**
|
||||
- Create: `apps/lib/data/repositories/inbox_repository.dart`
|
||||
- Create: `apps/lib/data/repositories/calendar_event_repository.dart`
|
||||
- Create: `apps/lib/data/repositories/user_repository.dart`
|
||||
- Modify: `apps/lib/app/di/injection.dart`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add a DI registration smoke test (or lightweight compile-level usage test) that resolves the new repositories from GetIt.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `flutter test <new-test-file>`
|
||||
|
||||
Expected: FAIL because repositories are not yet defined/registered.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Implement repository facades that wrap existing APIs (`InboxApi`, `CalendarApi`, `UsersApi`) and register them as singletons in DI.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `flutter test <new-test-file>`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/lib/data/repositories apps/lib/app/di/injection.dart
|
||||
git commit -m "refactor: add shared data repositories module"
|
||||
```
|
||||
|
||||
### Task 3: Migrate Cross-Feature Screens to Shared Repositories
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/lib/features/messages/presentation/screens/message_invite_detail_screen.dart`
|
||||
- Modify: `apps/lib/features/messages/presentation/screens/message_invite_list_screen.dart`
|
||||
- Modify: `apps/lib/features/todo/presentation/screens/todo_edit_screen.dart`
|
||||
|
||||
**Step 1: Write the failing test**
|
||||
|
||||
Add targeted tests for message detail/list data loading and todo edit schedule loading using repository dependencies.
|
||||
|
||||
**Step 2: Run test to verify it fails**
|
||||
|
||||
Run: `flutter test <affected-test-files>`
|
||||
|
||||
Expected: FAIL before dependency switch.
|
||||
|
||||
**Step 3: Write minimal implementation**
|
||||
|
||||
Replace direct API injections with shared repository injections from `apps/lib/data/repositories/*`.
|
||||
|
||||
**Step 4: Run test to verify it passes**
|
||||
|
||||
Run: `flutter test <affected-test-files>`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
**Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/lib/features/messages/presentation/screens/message_invite_detail_screen.dart apps/lib/features/messages/presentation/screens/message_invite_list_screen.dart apps/lib/features/todo/presentation/screens/todo_edit_screen.dart
|
||||
git commit -m "refactor: switch cross-feature screens to shared repositories"
|
||||
```
|
||||
|
||||
### Task 4: Verification and Cleanup
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/AGENTS.md`
|
||||
- Modify: `docs/bugs/2026-03-27-repository缓存抽象.md`
|
||||
- Modify: `docs/bugs/服务层与Repository层职责混乱.md`
|
||||
|
||||
**Step 1: Run full targeted verification**
|
||||
|
||||
Run: `flutter analyze lib/data/cache lib/data/repositories lib/app/di/injection.dart lib/features/messages/presentation/screens/message_invite_detail_screen.dart lib/features/messages/presentation/screens/message_invite_list_screen.dart lib/features/todo/presentation/screens/todo_edit_screen.dart`
|
||||
|
||||
Expected: No issues.
|
||||
|
||||
**Step 2: Run regression tests**
|
||||
|
||||
Run: `flutter test test/data/cache/cached_repository_test.dart test/features/settings/data/services/user_profile_cache_repository_test.dart test/app/router/app_router_redirect_test.dart`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
**Step 3: Update docs status**
|
||||
|
||||
Mark relevant bug docs as in-progress/resolved and document new layering rules.
|
||||
|
||||
**Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add apps/AGENTS.md docs/bugs/2026-03-27-repository缓存抽象.md docs/bugs/服务层与Repository层职责混乱.md
|
||||
git commit -m "docs: document shared repository and cache strategy"
|
||||
```
|
||||
Reference in New Issue
Block a user