Skip to content

feat: Implement device-specific dynamic theme switching - #620

Merged
AustinChangLinksys merged 6 commits into
dev-2.0.0from
peter/theme-config-611
Feb 11, 2026
Merged

feat: Implement device-specific dynamic theme switching#620
AustinChangLinksys merged 6 commits into
dev-2.0.0from
peter/theme-config-611

Conversation

@PeterJhongLinksys

Copy link
Copy Markdown
Collaborator

Summary

Implement reactive theme system that automatically switches themes based on router model number. The system dynamically loads device-specific themes when users log into different router models, with support for CI/CD environment overrides.

Changes

Core Implementation

  • deviceThemeConfigProvider: New Riverpod FutureProvider that reactively loads themes based on sessionProvider.modelNumber
  • BrandUtils.getDeviceTheme(): Extended BrandUtils with theme loading method using model-to-suffix mapping (TB-, CF, DU)
  • Theme Loading Priority: forcedSource (CI/CD) → device theme → default fallback
  • AsyncValue Handling: Proper loading/error/data state handling in app.dart

Technical Details

  • lib/providers/device_theme_config_provider.dart: FutureProvider with forcedSource priority check
  • lib/utils.dart: BrandUtils.getDeviceTheme() with TB/CF/DU model mapping
  • lib/app.dart: AsyncValue.when() for loading states, extracted _buildMaterialApp helper
  • lib/main.dart: Simplified to register default theme as fallback only
  • lib/di.dart: Updated documentation explaining theme system migration
  • build_web.sh: Support THEME_SOURCE and THEME_JSON parameters for CI/CD

Theme Mappings

  • TB- series: assets/theme/theme_tb.json
  • CF series: assets/theme/theme_cf.json
  • DU series (M60DU, M80DU, etc.): assets/theme/theme_du.json

Test Plan

  • Unit tests: 19/19 passing (11 BrandUtils + 8 Provider tests)
  • Test device theme loading for different model numbers
  • Test forcedSource priority (THEME_SOURCE environment variable)
  • Test error handling and default theme fallback
  • Test AsyncValue states (loading/error/data) in app.dart
  • Verify backward compatibility with existing theme infrastructure
  • Manual testing: Connect to TB/CF/DU series devices and verify theme switching
  • Manual testing: Verify loading indicator appears during theme load
  • CI/CD testing: Verify THEME_SOURCE override works in build pipeline

Architecture Compliance

✅ All changes comply with project constitution:

  • Article I: 19 unit tests with comprehensive coverage
  • Article III: Correct naming conventions (snake_case files, lowerCamelCase providers)
  • Article V: Minimal structure, extended existing BrandUtils, no over-engineering
  • Article VIII: Fast deterministic unit tests, no flaky tests
  • Article XII: Proper Riverpod usage with FutureProvider and select()

Breaking Changes

None. The implementation preserves backward compatibility:

  • GetIt theme registration maintained as fallback
  • Existing forcedSource functionality preserved
  • Default theme behavior unchanged for unmapped models

Migration Notes

  • Theme loading moved from static GetIt to reactive Riverpod provider
  • GetIt theme registration now serves as fallback only
  • Active theme management handled by deviceThemeConfigProvider

🤖 Generated with Claude Code

PeterJhongLinksys and others added 2 commits February 9, 2026 14:44
Add reactive theme system that automatically switches themes based on router model number.

Key changes:
- Add deviceThemeConfigProvider for reactive theme loading based on modelNumber
- Extend BrandUtils with getDeviceTheme() method using model-to-suffix mapping
- Integrate device theme provider in app.dart with AsyncValue error handling
- Support forcedSource priority for CI/CD theme overrides (THEME_SOURCE)
- Add theme_m60.json with glass style for M60 series devices
- Update build_web.sh to support THEME_SOURCE and THEME_JSON parameters
- Migrate from static GetIt theme loading to reactive Riverpod system
- Preserve backward compatibility with existing theme infrastructure

Technical implementation:
- lib/providers/device_theme_config_provider.dart: FutureProvider with forcedSource priority
- lib/utils.dart: BrandUtils.getDeviceTheme() with TB/CF/M60 model mapping
- lib/app.dart: AsyncValue.when() handling for loading/error/data states
- lib/main.dart: Simplified to register default theme as fallback only
- lib/di.dart: Updated documentation explaining theme migration
- assets/theme/: New directory structure for device-specific themes

Testing:
- Add 19 unit tests (11 BrandUtils + 8 Provider tests)
- All tests passing with comprehensive coverage
- test/utils/brand_utils_theme_test.dart: Theme loading and error handling
- test/providers/device_theme_config_provider_test.dart: Provider behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replace M60-specific theme mapping with DU series mapping for broader device support.

Changes:
- Update _modelSuffixMap: 'M60' -> 'DU' in BrandUtils
- Remove temporary theme_m60.json test file
- DU mapping will match M60DU, M80DU, and other DU series devices

This change aligns theme mapping with actual device model naming conventions
where 'DU' is the consistent identifier across the product line.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@qodo-code-review

Copy link
Copy Markdown

Review Summary by Qodo

Implement device-specific dynamic theme switching with Riverpod

✨ Enhancement

Grey Divider

Walkthroughs

Description
• Implement reactive device-specific theme switching via Riverpod provider
• Extend BrandUtils with getDeviceTheme() method for model-based theme loading
• Integrate deviceThemeConfigProvider in app.dart with AsyncValue state handling
• Support CI/CD theme overrides via THEME_SOURCE environment variable
• Add comprehensive unit tests for theme loading and provider behavior
Diagram
flowchart LR
  A["sessionProvider<br/>modelNumber"] -->|"watch"| B["deviceThemeConfigProvider"]
  C["ThemeConfigLoader<br/>forcedSource"] -->|"Priority 1"| B
  B -->|"Priority 2"| D["BrandUtils<br/>getDeviceTheme"]
  D -->|"model mapping"| E["Theme Assets<br/>TB/CF/DU"]
  E -->|"fallback"| F["Default Theme"]
  B -->|"AsyncValue"| G["app.dart<br/>_buildMaterialApp"]
  G -->|"loading/error/data"| H["MaterialApp<br/>with Theme"]
Loading

Grey Divider

File Changes

1. lib/providers/device_theme_config_provider.dart ✨ Enhancement +81/-0

New Riverpod provider for reactive device theme loading

lib/providers/device_theme_config_provider.dart


2. lib/app.dart ✨ Enhancement +48/-6

Integrate device theme provider with AsyncValue handling

lib/app.dart


3. lib/utils.dart ✨ Enhancement +54/-0

Add getDeviceTheme method with model-to-suffix mapping

lib/utils.dart


View more (6)
4. lib/main.dart ✨ Enhancement +4/-4

Simplify theme loading to use reactive provider

lib/main.dart


5. lib/di.dart 📝 Documentation +16/-2

Update documentation for theme system migration

lib/di.dart


6. build_web.sh ⚙️ Configuration changes +4/-3

Add THEME_SOURCE parameter for CI/CD overrides

build_web.sh


7. pubspec.yaml ⚙️ Configuration changes +2/-0

Register assets/theme directory for device-specific themes

pubspec.yaml


8. test/providers/device_theme_config_provider_test.dart 🧪 Tests +125/-0

Add unit tests for device theme config provider

test/providers/device_theme_config_provider_test.dart


9. test/utils/brand_utils_theme_test.dart 🧪 Tests +118/-0

Add unit tests for BrandUtils theme loading method

test/utils/brand_utils_theme_test.dart


Grey Divider

Qodo Logo

@qodo-code-review

qodo-code-review Bot commented Feb 9, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (2) 📎 Requirement gaps (0)

Grey Divider


Action required

✅ 1. Theme assets outside allowed dirs 📘 Rule violation ⛯ Reliability
Description
pubspec.yaml adds assets/theme/, introducing a new asset directory outside the designated
assets/resources/ (and assets/icons/) locations. • This breaks the documented asset organization
policy and can lead to inconsistent asset placement and harder-to-manage builds.
Code

pubspec.yaml[137]

+    - assets/theme/
Evidence
The compliance checklist requires general resources to be stored under assets/resources/ (icons
under assets/icons/). The PR adds a new top-level assets/theme/ directory to Flutter assets,
which is outside those designated directories.

CLAUDE.md
pubspec.yaml[132-138]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR adds a new Flutter asset directory `assets/theme/`, but the compliance checklist requires general resources to live under `assets/resources/`.
## Issue Context
Theme JSON files are general resources and should follow the repo’s asset directory conventions to keep asset management consistent.
## Fix Focus Areas
- pubspec.yaml[132-138]
- lib/utils.dart[769-776]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


✅ 2. THEME_JSON ignored in normal 🐞 Bug ✓ Correctness
Description
• In the new flow, deviceThemeConfigProvider only calls ThemeConfigLoader.load() when
THEME_SOURCE is *not* normal; otherwise it always uses BrandUtils.getDeviceTheme(modelNumber).
• ThemeConfigLoader still supports the prior “priority” behavior where THEME_JSON alone
(non-empty) wins even when THEME_SOURCE=normal. • Result: builds that previously set only
THEME_JSON (without forcing THEME_SOURCE=cicd) will silently stop applying the override theme.
Code

lib/providers/device_theme_config_provider.dart[R59-76]

+  // Check for forced theme source (CI/CD, testing)
+  final forcedSource = ThemeConfigLoader.forcedSource;
+
+  // Priority 1: Use forced source if set (environment variable override)
+  if (forcedSource != ThemeSource.normal) {
+    logger.i('[DeviceThemeConfig] Using forcedSource: $forcedSource');
+    return ThemeConfigLoader.load();
+  }
+
+  // Priority 2: Device-specific theme based on model number
+  final modelNumber = ref.watch(
+    sessionProvider.select((state) => state.modelNumber),
+  );
+
+  logger.d('[DeviceThemeConfig] Loading device theme for model: $modelNumber');
+
+  // Load device theme via BrandUtils (reuses brand asset mapping logic)
+  final themeConfig = await BrandUtils.getDeviceTheme(modelNumber);
Evidence
ThemeConfigLoader implements a priority resolution that uses THEME_JSON even in normal mode, but
the new provider bypasses ThemeConfigLoader.load() entirely when forcedSource is normal, so
THEME_JSON is not considered unless THEME_SOURCE is explicitly set to a non-normal value.
Additionally, main.dart no longer loads ThemeConfigLoader.load() at startup, removing the old
behavior path.

lib/providers/device_theme_config_provider.dart[59-76]
lib/theme/theme_config_loader.dart[81-90]
lib/main.dart[77-80]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`deviceThemeConfigProvider` ignores `THEME_JSON` unless `THEME_SOURCE` is set to a non-normal value. Previously, `ThemeConfigLoader` would apply `THEME_JSON` in normal mode via priority resolution.
### Issue Context
This is a behavioral regression for CI/CD and local builds that set only `--dart-define=THEME_JSON=...` (and rely on default `THEME_SOURCE=normal`).
### Fix Focus Areas
- lib/providers/device_theme_config_provider.dart[59-76]
- lib/theme/theme_config_loader.dart[28-36]
### Suggested implementation
1. Add public getters in `ThemeConfigLoader` for env values (e.g., `static String get themeJsonEnv =&amp;amp;amp;amp;gt; _themeJsonEnv;` and similar for network url/asset path).
2. In `deviceThemeConfigProvider`, update the branch condition to also use `ThemeConfigLoader.themeJsonEnv.isNotEmpty` (and optionally other env signals) to decide when to call `ThemeConfigLoader.load()`.
3. (Optional) Add a short migration note in docs/build scripts stating that THEME_JSON-only builds must set THEME_SOURCE=cicd if you choose not to support the old behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Loading swaps out router 🐞 Bug ⛯ Reliability
Description
• While the theme provider is loading, app.dart returns a plain MaterialApp (no go_router)
rather than MaterialApp.router. • deviceThemeConfigProvider reloads whenever
sessionProvider.modelNumber changes (e.g., during prepare/login flows), so the app can transiently
replace the router-based app with a different MaterialApp after startup. • This can reset
navigation state/deeplinks and cause user-visible flicker; it also temporarily drops
localization/theme configuration present in _buildMaterialApp.
Code

lib/app.dart[R116-121]

+    return deviceThemeConfigAsync.when(
+      loading: () => MaterialApp(
+        home: Scaffold(
+          body: Center(child: CircularProgressIndicator()),
+        ),
+      ),
Evidence
The app’s root widget tree changes shape depending on async theme state. Because the theme provider
depends on sessionProvider.modelNumber, it can transition to loading at runtime when device info
is fetched/updated, and the app will temporarily not use MaterialApp.router.

lib/app.dart[116-121]
lib/providers/device_theme_config_provider.dart[68-71]
lib/route/router_provider.dart[370-374]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The root `build()` returns `MaterialApp` (non-router) during theme loading, and `MaterialApp.router` otherwise. Since the theme provider can reload when `modelNumber` changes, this can reset routing/navigation state and cause UI flicker.
### Issue Context
`deviceThemeConfigProvider` watches `sessionProvider.modelNumber`, which is updated during prepare/login flows. That means theme reloads are not only at startup.
### Fix Focus Areas
- lib/app.dart[113-142]
- lib/providers/device_theme_config_provider.dart[68-77]
### Suggested implementation
1. Always return `_buildMaterialApp(...)` (i.e., always use `MaterialApp.router`).
2. Choose a themeConfig even while loading (use `themeAsync.value ?? ThemeJsonConfig.defaultConfig()`).
3. Show loading UI as an overlay in the `builder` rather than swapping out the entire app widget.
4. Consider `AsyncValue.when(skipLoadingOnReload: true)` (or equivalent) to keep the previous theme during refresh.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

✅ 4. Log tags missing ]: format 📘 Rule violation ✓ Correctness
Description
• New log messages use bracketed tags like "[DeviceThemeConfig] ..." and "[BrandUtils] ..." but
omit the ]: delimiter expected by the app’s log tag parser. • This makes logs harder to
parse/audit and prevents them from being categorized properly in the web log cache.
Code

lib/providers/device_theme_config_provider.dart[R64-78]

+    logger.i('[DeviceThemeConfig] Using forcedSource: $forcedSource');
+    return ThemeConfigLoader.load();
+  }
+
+  // Priority 2: Device-specific theme based on model number
+  final modelNumber = ref.watch(
+    sessionProvider.select((state) => state.modelNumber),
+  );
+
+  logger.d('[DeviceThemeConfig] Loading device theme for model: $modelNumber');
+
+  // Load device theme via BrandUtils (reuses brand asset mapping logic)
+  final themeConfig = await BrandUtils.getDeviceTheme(modelNumber);
+
+  logger.i('[DeviceThemeConfig] Device theme loaded for $modelNumber');
Evidence
Secure logging requires logs to be structured for auditing. The repository’s logger explicitly
expects tagged messages in the format [TAG]:message, but the newly added log statements do not
match this format (missing ]:), reducing their structured/auditable value.

Rule 5: Generic: Secure Logging Practices
lib/core/utils/logger.dart[81-84]
lib/providers/device_theme_config_provider.dart[64-78]
lib/utils.dart[749-785]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New log statements do not follow the structured tag format expected by the logger (`[TAG]:message`). This prevents proper tag extraction and reduces audit/debug value.
## Issue Context
`lib/core/utils/logger.dart` parses tags using a regex that expects a colon after the closing bracket.
## Fix Focus Areas
- lib/providers/device_theme_config_provider.dart[64-78]
- lib/utils.dart[749-785]
- lib/core/utils/logger.dart[81-84]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


✅ 5. build_web arg breaking change 🐞 Bug ⛯ Reliability
Description
build_web.sh changed positional args from a single $7 theme value to $7=themeSource and
$8=themeJson. • Repo documentation still shows invoking ./build_web.sh without parameters or
only documents 6 parameters, which makes it easy to call the script incorrectly. • Without argument
validation/backward compatibility, existing callers that passed theme JSON as the 7th arg will
silently produce builds with THEME_SOURCE set to a JSON blob and empty THEME_JSON.
Code

build_web.sh[R20-21]

+themeSource=$7
+themeJson=$8
Evidence
The script’s interface changed, and docs in-repo are now out of date. Incorrect invocation will
silently misconfigure --dart-define values, disabling theme overrides.

build_web.sh[14-22]
build_web.sh[6-10]
README.md[69-76]
CLAUDE.md[21-25]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`build_web.sh` now expects 8 positional args but docs still show 0–6 args. This can silently misconfigure THEME_SOURCE/THEME_JSON, breaking theme overrides.
### Issue Context
The script is used for web builds and now wires the theme override env defines.
### Fix Focus Areas
- build_web.sh[14-22]
- README.md[69-76]
- CLAUDE.md[21-25]
### Suggested implementation
1. Add `usage()` + check `$#` at script start; exit non-zero with help text when insufficient args.
2. Backward compatibility:
- If `$# -eq 7`: set `themeSource=cicd` and `themeJson=$7` (or infer based on content), so existing callers keep working.
- If `$# -ge 8`: use `themeSource=$7`, `themeJson=$8`.
3. Update README/CLAUDE to document full parameter list and show an example for forcing theme via THEME_SOURCE/THEME_JSON.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment thread pubspec.yaml
- assets/icons/
- assets/resources/
- assets/a2ui/widgets/
- assets/theme/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Theme assets outside allowed dirs 📘 Rule violation ⛯ Reliability

pubspec.yaml adds assets/theme/, introducing a new asset directory outside the designated
  assets/resources/ (and assets/icons/) locations.
• This breaks the documented asset organization policy and can lead to inconsistent asset placement
  and harder-to-manage builds.
Agent Prompt
## Issue description
The PR adds a new Flutter asset directory `assets/theme/`, but the compliance checklist requires general resources to live under `assets/resources/`.

## Issue Context
Theme JSON files are general resources and should follow the repo’s asset directory conventions to keep asset management consistent.

## Fix Focus Areas
- pubspec.yaml[132-138]
- lib/utils.dart[769-776]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +59 to +76
// Check for forced theme source (CI/CD, testing)
final forcedSource = ThemeConfigLoader.forcedSource;

// Priority 1: Use forced source if set (environment variable override)
if (forcedSource != ThemeSource.normal) {
logger.i('[DeviceThemeConfig] Using forcedSource: $forcedSource');
return ThemeConfigLoader.load();
}

// Priority 2: Device-specific theme based on model number
final modelNumber = ref.watch(
sessionProvider.select((state) => state.modelNumber),
);

logger.d('[DeviceThemeConfig] Loading device theme for model: $modelNumber');

// Load device theme via BrandUtils (reuses brand asset mapping logic)
final themeConfig = await BrandUtils.getDeviceTheme(modelNumber);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Theme_json ignored in normal 🐞 Bug ✓ Correctness

• In the new flow, deviceThemeConfigProvider only calls ThemeConfigLoader.load() when
  THEME_SOURCE is *not* normal; otherwise it always uses BrandUtils.getDeviceTheme(modelNumber).
• ThemeConfigLoader still supports the prior “priority” behavior where THEME_JSON alone
  (non-empty) wins even when THEME_SOURCE=normal.
• Result: builds that previously set only THEME_JSON (without forcing THEME_SOURCE=cicd) will
  silently stop applying the override theme.
Agent Prompt
### Issue description
`deviceThemeConfigProvider` ignores `THEME_JSON` unless `THEME_SOURCE` is set to a non-normal value. Previously, `ThemeConfigLoader` would apply `THEME_JSON` in normal mode via priority resolution.

### Issue Context
This is a behavioral regression for CI/CD and local builds that set only `--dart-define=THEME_JSON=...` (and rely on default `THEME_SOURCE=normal`).

### Fix Focus Areas
- lib/providers/device_theme_config_provider.dart[59-76]
- lib/theme/theme_config_loader.dart[28-36]

### Suggested implementation
1. Add public getters in `ThemeConfigLoader` for env values (e.g., `static String get themeJsonEnv => _themeJsonEnv;` and similar for network url/asset path).
2. In `deviceThemeConfigProvider`, update the branch condition to also use `ThemeConfigLoader.themeJsonEnv.isNotEmpty` (and optionally other env signals) to decide when to call `ThemeConfigLoader.load()`.
3. (Optional) Add a short migration note in docs/build scripts stating that THEME_JSON-only builds must set THEME_SOURCE=cicd if you choose not to support the old behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread lib/app.dart Outdated
Comment on lines +116 to +121
return deviceThemeConfigAsync.when(
loading: () => MaterialApp(
home: Scaffold(
body: Center(child: CircularProgressIndicator()),
),
),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Loading swaps out router 🐞 Bug ⛯ Reliability

• While the theme provider is loading, app.dart returns a plain MaterialApp (no go_router)
  rather than MaterialApp.router.
• deviceThemeConfigProvider reloads whenever sessionProvider.modelNumber changes (e.g., during
  prepare/login flows), so the app can transiently replace the router-based app with a different
  MaterialApp after startup.
• This can reset navigation state/deeplinks and cause user-visible flicker; it also temporarily
  drops localization/theme configuration present in _buildMaterialApp.
Agent Prompt
### Issue description
The root `build()` returns `MaterialApp` (non-router) during theme loading, and `MaterialApp.router` otherwise. Since the theme provider can reload when `modelNumber` changes, this can reset routing/navigation state and cause UI flicker.

### Issue Context
`deviceThemeConfigProvider` watches `sessionProvider.modelNumber`, which is updated during prepare/login flows. That means theme reloads are not only at startup.

### Fix Focus Areas
- lib/app.dart[113-142]
- lib/providers/device_theme_config_provider.dart[68-77]

### Suggested implementation
1. Always return `_buildMaterialApp(...)` (i.e., always use `MaterialApp.router`).
2. Choose a themeConfig even while loading (use `themeAsync.value ?? ThemeJsonConfig.defaultConfig()`).
3. Show loading UI as an overlay in the `builder` rather than swapping out the entire app widget.
4. Consider `AsyncValue.when(skipLoadingOnReload: true)` (or equivalent) to keep the previous theme during refresh.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

PeterJhongLinksys and others added 4 commits February 9, 2026 18:20
This commit resolves issues identified in PR #620 code review:

1. Fix THEME_JSON ignored in normal mode
   - Add shouldUseDeviceTheme() method to ThemeConfigLoader
   - Check all environment overrides (THEME_SOURCE, THEME_JSON, THEME_NETWORK_URL)
   - Fixes regression where THEME_SOURCE=normal + THEME_JSON was ignored

2. Fix navigation reset during theme loading
   - Always use MaterialApp.router in all AsyncValue states
   - Use default theme during loading instead of non-router MaterialApp
   - Prevents navigation state loss when modelNumber changes

3. Standardize log format consistency
   - Update all theme-related logs to [TAG]: message format
   - Aligns with logger.dart regex pattern for proper parsing

4. Update documentation for build_web.sh parameters
   - Add themeSource and themeJson parameter documentation
   - Include usage examples in README.md and CLAUDE.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
… provider (#611)

- Convert ThemeConfigLoader from static-only class to instance class
  with forTesting() constructor for dependency injection in tests
- Move BrandUtils.getDeviceTheme() and _modelSuffixMap into
  ThemeConfigLoader as the single entry point for theme resolution
- Rename deviceThemeConfigProvider to themeConfigProvider
- Remove ThemeConfigLoader.load() calls from demo entry points
  (main_demo.dart, main_usp_demo.dart); theme now loads via provider
- Simplify di.dart: remove themeConfig parameter from dependencySetup()
- Remove unused BrandUtils.getDeviceTheme() from utils.dart
- Update tests to use ThemeConfigLoader.forTesting() constructor
- Delete obsolete brand_utils_theme_test.dart and
  device_theme_config_provider_test.dart
- Document the new theme loading flow and architecture
- Explain ThemeConfigLoader and themeConfigProvider roles
- Diagram the decision tree and override priorities
@PeterJhongLinksys PeterJhongLinksys linked an issue Feb 10, 2026 that may be closed by this pull request

@AustinChangLinksys AustinChangLinksys left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good to me

@AustinChangLinksys
AustinChangLinksys merged commit 3ad87c8 into dev-2.0.0 Feb 11, 2026
2 checks passed
@AustinChangLinksys
AustinChangLinksys deleted the peter/theme-config-611 branch February 11, 2026 13:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Integrate Theme config

2 participants