Skip to main content
Chromia UI provides built-in support for light and dark themes with automatic color adjustments that ensure proper contrast and readability.

Quick Start

The simplest way to implement light and dark themes is using the built-in factory constructors:
import 'package:chromia_ui/chromia_ui.dart';

// Light theme
final lightTheme = ChromiaThemeData.light();

// Dark theme
final darkTheme = ChromiaThemeData.dark();

Theme Brightness

Every ChromiaThemeData has a brightness property that indicates whether it’s a light or dark theme:
final theme = ChromiaThemeData.light();

print(theme.brightness); // Brightness.light
print(theme.isLight);    // true
print(theme.isDark);     // false

Implementing Theme Toggle

Here’s a complete example showing how to implement a theme toggle:
import 'package:flutter/material.dart';
import 'package:chromia_ui/chromia_ui.dart';

class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool isDarkMode = false;

  void toggleTheme() {
    setState(() {
      isDarkMode = !isDarkMode;
    });
  }

  @override
  Widget build(BuildContext context) {
    final chromiaTheme = isDarkMode 
      ? ChromiaThemeData.dark() 
      : ChromiaThemeData.light();

    return ChromiaTheme(
      data: chromiaTheme,
      child: MaterialApp(
        theme: chromiaTheme.toMaterialTheme(),
        home: HomePage(onToggleTheme: toggleTheme),
      ),
    );
  }
}

System Theme Detection

You can detect the system’s preferred theme and respect it:
import 'package:flutter/material.dart';
import 'package:chromia_ui/chromia_ui.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // Get system brightness
    final brightness = MediaQuery.platformBrightnessOf(context);
    final isDarkMode = brightness == Brightness.dark;
    
    final chromiaTheme = isDarkMode 
      ? ChromiaThemeData.dark() 
      : ChromiaThemeData.light();

    return ChromiaTheme(
      data: chromiaTheme,
      child: MaterialApp(
        theme: chromiaTheme.toMaterialTheme(),
        home: HomePage(),
      ),
    );
  }
}

Light vs Dark Color Differences

Chromia UI automatically adjusts colors for optimal contrast in light and dark modes:

Primary Colors

primary: ColorTokens.primary600    // Darker shade for light backgrounds
primaryHover: ColorTokens.primary800
primaryPressed: ColorTokens.primary900
onPrimary: ColorTokens.neutral0    // White text on primary

Surface Colors

surface: ColorTokens.neutral0      // White
background: ColorTokens.neutral50  // Off-white
onSurface: ColorTokens.neutral900  // Dark text

Text Colors

textPrimary: ColorTokens.neutral900    // Dark text
textSecondary: ColorTokens.neutral700  // Medium gray
textTertiary: ColorTokens.neutral500   // Light gray

Theme-Aware Widgets

Use context extensions to build widgets that automatically adapt to the current theme:
class ThemedCard extends StatelessWidget {
  final String title;
  final String subtitle;

  const ThemedCard({
    required this.title,
    required this.subtitle,
  });

  @override
  Widget build(BuildContext context) {
    final colors = context.chromiaColors;
    final typography = context.chromiaTypography;
    final spacing = context.chromiaSpacing;
    final radius = context.chromiaRadius;
    final shadows = context.chromiaShadows;

    return Container(
      padding: spacing.paddingL,
      decoration: BoxDecoration(
        color: colors.surface,
        borderRadius: radius.radiusM,
        border: Border.all(color: colors.border),
        boxShadow: shadows.s,
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            title,
            style: typography.titleMedium.copyWith(
              color: colors.textPrimary,
            ),
          ),
          spacing.gapVS,
          Text(
            subtitle,
            style: typography.bodyMedium.copyWith(
              color: colors.textSecondary,
            ),
          ),
        ],
      ),
    );
  }
}

Conditional Rendering

Sometimes you need to render different content based on the theme:
class ThemeAwareIcon extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final isDark = context.isDarkTheme;

    return Icon(
      isDark ? Icons.dark_mode : Icons.light_mode,
      color: context.chromiaColors.textPrimary,
    );
  }
}

Custom Colors for Dark Mode

You can create custom color schemes that work differently in light and dark modes:
ChromiaThemeData createCustomTheme(bool isDark) {
  final baseTheme = isDark 
    ? ChromiaThemeData.dark() 
    : ChromiaThemeData.light();

  return baseTheme.copyWith(
    colors: baseTheme.colors.copyWith(
      primary: isDark 
        ? Color(0xFF90CAF9)  // Lighter blue for dark mode
        : Color(0xFF1976D2), // Darker blue for light mode
    ),
  );
}

Shadows in Dark Mode

Chromia UI provides different shadow configurations for light and dark themes:
final lightShadows = ChromiaShadows.light();
final darkShadows = ChromiaShadows.dark();

// Dark mode shadows are slightly more prominent
// to provide better depth perception
In dark mode, shadows use a slightly higher opacity to ensure they remain visible against dark backgrounds.

Persisting Theme Preference

To persist the user’s theme choice, use shared preferences or a similar storage solution:
import 'package:shared_preferences/shared_preferences.dart';

class ThemePreferences {
  static const String _keyIsDarkMode = 'isDarkMode';

  static Future<bool> getThemePreference() async {
    final prefs = await SharedPreferences.getInstance();
    return prefs.getBool(_keyIsDarkMode) ?? false;
  }

  static Future<void> setThemePreference(bool isDarkMode) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setBool(_keyIsDarkMode, isDarkMode);
  }
}

class ThemeNotifier extends ChangeNotifier {
  bool _isDarkMode = false;
  
  ThemeNotifier() {
    _loadThemePreference();
  }
  
  Future<void> _loadThemePreference() async {
    _isDarkMode = await ThemePreferences.getThemePreference();
    notifyListeners();
  }
  
  bool get isDarkMode => _isDarkMode;
  
  Future<void> toggleTheme() async {
    _isDarkMode = !_isDarkMode;
    await ThemePreferences.setThemePreference(_isDarkMode);
    notifyListeners();
  }
}

Best Practices

Use semantic color names like colors.textPrimary instead of hardcoded colors. This ensures your UI adapts correctly to theme changes.
// Good ✓
Text('Hello', style: TextStyle(color: context.chromiaColors.textPrimary));

// Bad ✗
Text('Hello', style: TextStyle(color: Colors.black));
Always test your UI in both light and dark themes to ensure proper contrast and readability.
Consider respecting the system theme by default, and provide users with the option to override it.
Prefer context extensions (context.chromiaColors) over ChromiaTheme.of(context).colors for cleaner code.

Next Steps

Custom Themes

Create fully custom themes

Design Tokens

Explore available color tokens

Build docs developers (and LLMs) love