AgentSkillsCN

apply-theme

当为应用应用品牌主题配置时,可使用此功能。读取 theme_config.json 文件,并以新的颜色、渐变、Logo 以及动画效果更新 Dart 主题文件。

SKILL.md
--- frontmatter
name: apply-theme
description: Use when applying a brand theme configuration to the app. Reads theme_config.json and updates Dart theme files with new colors, gradients, logos, and animation effects.

Apply Theme Skill

Reads assets/config/theme_config.json generated by /brand-theme and applies it to the Flutter app's theme system.

When to Use

  • After running /brand-theme to generate a config
  • User asks to "apply the theme", "update theme from config", or "implement the brand colors"
  • User invokes /apply-theme directly

Prerequisites

  • assets/config/theme_config.json must exist (generated by /brand-theme)
  • If it doesn't exist, instruct user to run /brand-theme first

Process Overview

  1. Read and validate theme_config.json
  2. Update color definitions in lib/core/theme/app_colors.dart
  3. Update gradients in lib/core/theme/gradients.dart
  4. Update logo paths
  5. Update app name, caption, and welcome message in lib/core/constants/app_constants.dart
  6. Implement animation effects based on style preset
  7. Verify the app builds successfully

Step 1: Read Configuration

Read assets/config/theme_config.json and validate structure:

json
{
  "brand": {
    "name": "...",
    "appName": { "light": "...", "dark": "..." },
    "caption": { "light": "...", "dark": "..." },
    "welcomeMessage": "...",
    "logo": { "light": "...", "dark": "..." }
  },
  "colors": { "primary": "#...", "secondary": "#...", "accent": "#..." },
  "animation": { "style": "neon|cosmic|minimal|professional|playful" },
  "modes": { "default": "dark|light|system" }
}

Validation checks:

  • Primary color is valid hex
  • Logo paths are provided
  • Animation style is one of the valid presets
  • App name and caption are provided (or default to brand.name)

Step 2: Update app_colors.dart

File: lib/core/theme/app_colors.dart

Color Mapping

Map config colors to the app's color system. Generate both light and dark variants.

From config primary color, derive:

For dark mode (if primary is the accent color):

  • _darkPrimary = config primary
  • _darkPrimaryForeground = contrasting color (white or dark based on luminance)
  • _darkSecondary = config secondary (or derive complementary)
  • _darkAccent = config accent (or derive triadic)
  • _darkBackground = deep dark version of primary (10% lightness)
  • _darkCard = slightly lighter than background (15% lightness)
  • _darkMuted = desaturated primary at 20% lightness
  • _darkMessageBubbleOwn = primary at 30% lightness

For light mode:

  • _lightPrimary = config primary
  • _lightPrimaryForeground = white (or dark if primary is light)
  • _lightBackground = white or very light tint of primary
  • _lightCard = off-white or very light gray
  • _lightMessageBubbleOwn = config primary

Logo Paths

Update lines 97-98 in app_colors.dart:

dart
static const String lightLogo = '[CONFIG_LIGHT_LOGO]';
static const String darkLogo = '[CONFIG_DARK_LOGO]';

Logo Sizing Note:

  • The welcome screen (lib/features/chat/presentation/widgets/welcome_hero_screen.dart) displays the logo at height: 120
  • Prefer emblem/icon-only logos - full logos with text appear oversized at this height
  • If logo appears too large, verify an emblem-only asset is being used

Color Generation Algorithm

code
Given primary hex color:

1. Convert to HSL
2. For dark mode derived colors:
   - Background: H same, S * 0.3, L = 5%
   - Card: H same, S * 0.3, L = 8%
   - Muted: H same, S * 0.2, L = 15%
   - Border: H same, S * 0.2, L = 12%

3. For light mode derived colors:
   - Background: white (#FFFFFF)
   - Card: H same, S * 0.05, L = 98%
   - Muted: H same, S * 0.1, L = 96%

4. For foreground/text colors:
   - If primary luminance > 0.5: use dark text
   - If primary luminance <= 0.5: use light text

Step 3: Update gradients.dart

File: lib/core/theme/gradients.dart

Generate gradient variations from the config colors:

dart
// Primary gradient - 3 stops from lighter to darker
static const darkPrimaryGradient = LinearGradient(
  colors: [
    Color(0xFF[LIGHTER_PRIMARY]),  // +10% lightness
    Color(0xFF[PRIMARY]),           // config primary
    Color(0xFF[DARKER_PRIMARY]),    // -10% lightness
  ],
  stops: [0.0, 0.5, 1.0],
);

Apply same pattern to:

  • darkPrimaryGradient / lightPrimaryGradient
  • darkSecondaryGradient / lightSecondaryGradient
  • darkAccentGradient
  • primaryButtonGradient
  • secondaryButtonGradient

Step 4: Update App Constants

File: lib/core/constants/app_constants.dart

Update the app name, caption, and related strings from the config:

dart
// App Information
static const String appName = '[CONFIG_APPNAME_LIGHT]';           // Light theme name
static const String appNameDark = "[CONFIG_APPNAME_DARK]";        // Dark theme name
static const String appCaption = '[CONFIG_CAPTION_LIGHT]';        // Light theme caption
static const String appCaptionDark = '[CONFIG_CAPTION_DARK]';     // Dark theme caption

Mapping from config:

  • appName = brand.appName.light (or brand.name if not specified)
  • appNameDark = brand.appName.dark (or brand.name if not specified)
  • appCaption = brand.caption.light (or "Powered by AI" if not specified)
  • appCaptionDark = brand.caption.dark (or same as light if not specified)

Note: The welcome message (brand.welcomeMessage) is used in chat_home_page.dart via the app name constants. Updating appName and appNameDark will automatically update the welcome message format: "Welcome to {appName}!"

Step 5: Implement Animation Effects

Based on animation.style from config, create or update animation widgets.

CRITICAL: Follow Existing Codebase Patterns

All effects MUST follow these patterns from the existing codebase:

Pattern 1: Theme Brightness Detection

dart
final isDark = Theme.of(context).brightness == Brightness.dark;

Pattern 2: AppColors Extension Access

dart
final appColors = Theme.of(context).extension<AppColorsExtension>()!;
final colorScheme = Theme.of(context).colorScheme;

Pattern 3: Dynamic Color Selection (from speech_to_text_widget.dart)

dart
List<Color> _buildGlowColors() {
  final isDark = Theme.of(context).brightness == Brightness.dark;
  final appColors = context.appColors;

  if (isDark) {
    return isActive
      ? [
          colorScheme.primary.withOpacity(0.8),
          colorScheme.secondary.withOpacity(0.6),
          appColors.accent.withOpacity(0.4),
        ]
      : [
          appColors.mutedForeground.withOpacity(0.3),
          appColors.mutedForeground.withOpacity(0.2),
        ];
  }
  // Light theme with reduced intensity...
}

Pattern 4: Use AppConstants for Animation Values

Reference: lib/core/constants/app_constants.dart

dart
static const Duration glowAnimationDuration = Duration(milliseconds: 2000);
static const double glowBorderSize = 2.0;
static const double speechGlowSizeListening = 12.0;
static const double speechGlowSizeIdle = 6.0;

Pattern 5: Existing Packages to Use

  • glowy_borders - AnimatedGradientBorder for neon glow effects
  • flutter_animate - .animate().fadeIn().scale().shimmer() chains
  • CustomPainter - For particle/starfield effects (see particles_widget.dart)
  • AnimationController + AnimatedBuilder - For wave/pulse effects

Pattern 6: Particles Widget Reference

See lib/shared/widgets/particles_widget.dart for starfield implementation:

dart
ParticlesWidget(
  quantity: 50,
  color: appColors.primary,  // Theme-aware
  staticity: 50,
  size: 0.4,
)

Effect Implementation Patterns

Reference: https://github.com/flutterfx/flutterfx_widgets

cosmic style

Create lib/shared/widgets/effects/cosmic_background.dart:

  • Animated starfield using CustomPainter
  • Subtle parallax on mouse/touch movement
  • Use flutter_animate for particle fade effects

neon style

Create lib/shared/widgets/effects/border_beams.dart:

  • Animated gradient border using glowy_borders package
  • Pulsing glow effect synced with primary color
  • Use config primary color for the beam color

minimal style

  • No new widgets needed
  • Ensure existing BlurFade transitions are subtle (duration: 200ms, blur: 2.0)

professional style

Create lib/shared/widgets/effects/fancy_card.dart:

  • Elevated card with subtle shadow animation
  • Hover state with slight scale and shadow increase
  • Clean, understated motion

playful style

Create lib/shared/widgets/effects/particle_burst.dart:

  • Confetti/particle effect on interactions
  • Use chart colors from config for particle variety
  • Bouncy spring animations

Effect File Structure

code
lib/shared/widgets/effects/
├── cosmic_background.dart    # For cosmic style
├── border_beams.dart         # For neon style
├── fancy_card.dart           # For professional style
├── particle_burst.dart       # For playful style
└── effects_provider.dart     # Exports based on active style

effects_provider.dart Template

dart
import 'package:flutter/material.dart';

enum AnimationStyle { cosmic, neon, minimal, professional, playful }

// Current active style from config
const AnimationStyle activeStyle = AnimationStyle.[CONFIG_STYLE];

class EffectsProvider {
  static Widget wrapWithBackground(Widget child) {
    switch (activeStyle) {
      case AnimationStyle.cosmic:
        return CosmicBackground(child: child);
      case AnimationStyle.neon:
        return BorderBeamsBackground(child: child);
      case AnimationStyle.professional:
        return FancyCardBackground(child: child);
      case AnimationStyle.playful:
        return FlickeringGridBackground(child: child);
      case AnimationStyle.minimal:
      default:
        return child;
    }
  }

  static Widget wrapWithLoadingEffect(Widget child) {
    // Similar switch for loading indicators
  }
}

Step 6: Verify Build

After making all changes:

  1. Run flutter analyze to check for errors
  2. Run flutter build web --wasm to verify build succeeds
  3. Report any issues found

IMPORTANT: If testing via the backend server (http://localhost:8000):

  • The server serves static files from build/web
  • After rebuilding, restart the server to serve the new build
  • Kill existing server: lsof -ti :8000 | xargs kill -9
  • Start fresh: cd backend && uv run python app.py

Output Summary

After applying theme, display:

code
Theme applied successfully!

Updated files:
- lib/core/theme/app_colors.dart (colors + logos)
- lib/core/theme/gradients.dart (gradient variations)
- lib/core/constants/app_constants.dart (app name + caption)
- lib/shared/widgets/effects/ (animation widgets)

Brand applied:
- App Name: [NAME]
- Caption: [CAPTION]
- Welcome: [WELCOME_MESSAGE]

Colors applied:
- Primary: [HEX]
- Secondary: [HEX]
- Accent: [HEX]

Animation style: [STYLE]

Next steps:
- Run `flutter build web --wasm` to rebuild
- Restart the backend server to serve the new build
- Test the app at http://localhost:8000

Rollback

If issues occur, the skill should:

  1. Keep original files backed up (git handles this)
  2. Provide instructions to revert: git checkout -- lib/core/theme/

File Modification Checklist

Before completing, verify these files were updated:

  • lib/core/theme/app_colors.dart - Color constants and logo paths
  • lib/core/theme/gradients.dart - Gradient definitions
  • lib/core/constants/app_constants.dart - App name, caption, welcome message
  • lib/shared/widgets/effects/ - Animation effect widgets (if style != minimal)
  • Verify flutter analyze passes
  • Commit changes with descriptive message

Error Handling

Config file not found:

code
theme_config.json not found at assets/config/theme_config.json

Run /brand-theme first to generate the configuration.

Invalid color format:

code
Invalid color "[value]" in config. Expected hex format like #FF5F46.
Please fix the config file or run /brand-theme again.

Build fails after changes:

code
Build failed after applying theme. Errors:
[error details]

You can revert changes with: git checkout -- lib/core/theme/
Then review the config and try again.