Creating a Custom Theme
There are several approaches to creating custom themes:1. Modifying Built-in Themes
The simplest approach is to start with a built-in theme and customize it:import 'package:chromia_ui/chromia_ui.dart';
import 'package:flutter/material.dart';
final customTheme = ChromiaThemeData.light().copyWith(
colors: ChromiaThemeData.light().colors.copyWith(
primary: Color(0xFF6200EE),
secondary: Color(0xFF03DAC6),
),
);
2. Building from Primary Color
Create a theme from a single primary color:final customColors = ChromiaColors.fromPrimary(
Color(0xFF6200EE),
isDark: false,
);
final customTheme = ChromiaThemeData.light().copyWith(
colors: customColors,
);
3. Complete Custom Configuration
For full control, create a completely custom theme:final customColors = ChromiaColors(
// Brand colors
primary: Color(0xFF6200EE),
primaryHover: Color(0xFF7722FF),
primaryPressed: Color(0xFF4500BB),
primaryDisabled: Color(0xFFD5B8FF),
onPrimary: Color(0xFFFFFFFF),
secondary: Color(0xFF03DAC6),
secondaryHover: Color(0xFF00E5CC),
secondaryPressed: Color(0xFF00B8A0),
secondaryDisabled: Color(0xFFB2F2EE),
onSecondary: Color(0xFF000000),
// ... additional colors
surface: Color(0xFFFFFFFF),
surfaceHover: Color(0xFFF5F5F5),
surfacePressed: Color(0xFFEEEEEE),
surfaceVariant: Color(0xFFF5F5F5),
surfaceContainer: Color(0xFFEEEEEE),
surfaceContainerHigh: Color(0xFFEEEEEE),
surfaceContainerHighest: Color(0xFFE0E0E0),
onSurface: Color(0xFF2A2A2A),
onSurfaceVariant: Color(0xFF616161),
background: Color(0xFFFAFAFA),
onBackground: Color(0xFF2A2A2A),
// State colors
success: Color(0xFF43A047),
successHover: Color(0xFF388E3C),
onSuccess: Color(0xFFFFFFFF),
warning: Color(0xFFFB8C00),
warningHover: Color(0xFFF57C00),
onWarning: Color(0xFFFFFFFF),
error: Color(0xFFE53935),
errorHover: Color(0xFFD32F2F),
onError: Color(0xFFFFFFFF),
info: Color(0xFF00ACC1),
infoHover: Color(0xFF0097A7),
onInfo: Color(0xFFFFFFFF),
// Borders and dividers
border: Color(0xFFE0E0E0),
borderHover: Color(0xFFBDBDBD),
borderStrong: Color(0xFF9E9E9E),
divider: Color(0xFFEEEEEE),
// Text colors
textPrimary: Color(0xFF2A2A2A),
textSecondary: Color(0xFF616161),
textTertiary: Color(0xFF9E9E9E),
textDisabled: Color(0xFF9E9E9E),
// Other
shadow: Color(0xFF2A2A2A),
overlay: Color(0x52000000),
scrim: Color(0x52000000),
transparent: Color(0x00FFFFFF),
);
final customTheme = ChromiaThemeData(
colors: customColors,
typography: ChromiaTypography.defaultTypography(),
spacing: ChromiaSpacing.defaultSpacing(),
radius: ChromiaRadius.defaultRadius(),
shadows: ChromiaShadows.light(),
brightness: Brightness.light,
);
Customizing Typography
Create custom typography with your own font family and sizes:import 'package:google_fonts/google_fonts.dart';
final customTypography = ChromiaTypography(
displayLarge: GoogleFonts.inter(
fontSize: 72,
fontWeight: FontWeight.w700,
height: 1.2,
letterSpacing: -0.5,
),
displayMedium: GoogleFonts.inter(
fontSize: 56,
fontWeight: FontWeight.w700,
height: 1.2,
),
// ... other text styles
bodyLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1.5,
),
bodyMedium: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.5,
),
code: GoogleFonts.robotoMono(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.75,
),
// ... additional styles
);
final customTheme = ChromiaThemeData.light().copyWith(
typography: customTypography,
);
Customizing Spacing
Adjust spacing values to match your design requirements:final customSpacing = ChromiaSpacing(
none: 0,
xxs: 2,
xs: 4,
s: 8,
m: 16, // Increased from default 12
l: 24, // Increased from default 16
xl: 32, // Increased from default 20
xxl: 40, // Increased from default 24
xxxl: 48, // Increased from default 32
huge: 64,
xhuge: 80,
xxhuge: 96,
massive: 128,
);
final customTheme = ChromiaThemeData.light().copyWith(
spacing: customSpacing,
);
Customizing Border Radius
Create more or less rounded corners throughout your app:// More rounded (modern look)
final roundedRadius = ChromiaRadius(
none: 0,
xs: 4,
s: 8,
m: 12,
l: 16,
xl: 24,
xxl: 32,
full: 9999,
);
// Less rounded (classic look)
final sharpRadius = ChromiaRadius(
none: 0,
xs: 2,
s: 4,
m: 6,
l: 8,
xl: 10,
xxl: 12,
full: 9999,
);
final customTheme = ChromiaThemeData.light().copyWith(
radius: roundedRadius,
);
Customizing Shadows
Adjust shadow intensity for different elevation effects:final subtleShadows = ChromiaShadows(
none: [],
xs: [
BoxShadow(
color: Color(0x0D000000), // Lower opacity
blurRadius: 2,
offset: Offset(0, 1),
),
],
s: [
BoxShadow(
color: Color(0x0D000000),
blurRadius: 4,
offset: Offset(0, 2),
),
],
m: [
BoxShadow(
color: Color(0x0D000000),
blurRadius: 8,
offset: Offset(0, 4),
),
],
// ... other shadow levels
);
final customTheme = ChromiaThemeData.light().copyWith(
shadows: subtleShadows,
);
Theme Presets
Create reusable theme presets for different use cases:class AppThemes {
// Modern theme with vibrant colors
static ChromiaThemeData get modern {
return ChromiaThemeData.light().copyWith(
colors: ChromiaColors.fromPrimary(
Color(0xFF6200EE),
isDark: false,
),
radius: ChromiaRadius(
none: 0,
xs: 4,
s: 8,
m: 16,
l: 24,
xl: 32,
xxl: 40,
full: 9999,
),
);
}
// Classic theme with subtle colors
static ChromiaThemeData get classic {
return ChromiaThemeData.light().copyWith(
colors: ChromiaColors.fromPrimary(
Color(0xFF1976D2),
isDark: false,
),
radius: ChromiaRadius(
none: 0,
xs: 2,
s: 4,
m: 6,
l: 8,
xl: 10,
xxl: 12,
full: 9999,
),
);
}
// Minimal theme with lots of whitespace
static ChromiaThemeData get minimal {
return ChromiaThemeData.light().copyWith(
spacing: ChromiaSpacing(
none: 0,
xxs: 4,
xs: 8,
s: 16,
m: 24,
l: 32,
xl: 48,
xxl: 64,
xxxl: 80,
huge: 96,
xhuge: 128,
xxhuge: 160,
massive: 200,
),
);
}
}
// Usage
final theme = AppThemes.modern;
Matching Dark Theme
When creating a custom light theme, create a matching dark theme:class MyCustomTheme {
static ChromiaThemeData light() {
final customColors = ChromiaColors.fromPrimary(
Color(0xFF6200EE),
isDark: false,
);
return ChromiaThemeData(
colors: customColors,
typography: ChromiaTypography.defaultTypography(),
spacing: ChromiaSpacing.defaultSpacing(),
radius: ChromiaRadius.defaultRadius(),
shadows: ChromiaShadows.light(),
brightness: Brightness.light,
);
}
static ChromiaThemeData dark() {
final customColors = ChromiaColors.fromPrimary(
Color(0xFF6200EE),
isDark: true,
);
return ChromiaThemeData(
colors: customColors,
typography: ChromiaTypography.defaultTypography(),
spacing: ChromiaSpacing.defaultSpacing(),
radius: ChromiaRadius.defaultRadius(),
shadows: ChromiaShadows.dark(),
brightness: Brightness.dark,
);
}
}
// Usage
final lightTheme = MyCustomTheme.light();
final darkTheme = MyCustomTheme.dark();
Using Custom Fonts
Integrate custom fonts using Google Fonts or local font files:import 'package:google_fonts/google_fonts.dart';
final customTypography = ChromiaTypography.defaultTypography().copyWith(
displayLarge: GoogleFonts.playfairDisplay(
fontSize: 72,
fontWeight: FontWeight.w700,
height: 1.2,
letterSpacing: -0.5,
),
bodyLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1.5,
),
);
Theming Best Practices
Maintain semantic color names
Maintain semantic color names
When creating custom colors, maintain the semantic naming convention (primary, secondary, success, error, etc.) to ensure components work correctly.
Test accessibility
Test accessibility
Ensure your custom colors meet WCAG contrast requirements:
// Use Flutter's contrast ratio calculator
import 'package:flutter/material.dart';
final ratio = ThemeData.estimateBrightnessForColor(
foreground: colors.textPrimary,
background: colors.surface,
);
// Aim for at least 4.5:1 for normal text
// and 3:1 for large text
Create consistent spacing
Create consistent spacing
Use a consistent spacing scale (like the 4px or 8px grid) to maintain visual harmony.
Document your theme
Document your theme
Create documentation for your custom theme to help other developers understand the design system:
/// Custom theme for the MyApp application.
///
/// Uses a purple primary color (#6200EE) and increased
/// spacing for a more spacious feel.
///
/// Example:
/// ```dart
/// ChromiaTheme(
/// data: MyAppTheme.light(),
/// child: MaterialApp(...),
/// )
/// ```
class MyAppTheme {
// ...
}
Complete Example
Here’s a complete example of a custom theme implementation:import 'package:chromia_ui/chromia_ui.dart';
import 'package:flutter/material.dart';
import 'package:google_fonts/google_fonts.dart';
class ECommerceTheme {
// Brand colors
static const Color _brandPrimary = Color(0xFFE91E63);
static const Color _brandSecondary = Color(0xFF9C27B0);
static ChromiaThemeData light() {
// Custom colors based on brand
final colors = ChromiaColors.fromPrimary(
_brandPrimary,
isDark: false,
).copyWith(
secondary: _brandSecondary,
);
// Custom typography with brand fonts
final typography = ChromiaTypography(
displayLarge: GoogleFonts.playfairDisplay(
fontSize: 72,
fontWeight: FontWeight.w700,
height: 1.2,
letterSpacing: -0.5,
),
displayMedium: GoogleFonts.playfairDisplay(
fontSize: 56,
fontWeight: FontWeight.w700,
height: 1.2,
),
displaySmall: GoogleFonts.playfairDisplay(
fontSize: 48,
fontWeight: FontWeight.w600,
height: 1.2,
),
headlineLarge: GoogleFonts.inter(
fontSize: 40,
fontWeight: FontWeight.w600,
height: 1.2,
),
headlineMedium: GoogleFonts.inter(
fontSize: 32,
fontWeight: FontWeight.w600,
height: 1.5,
),
headlineSmall: GoogleFonts.inter(
fontSize: 28,
fontWeight: FontWeight.w500,
height: 1.5,
),
titleLarge: GoogleFonts.inter(
fontSize: 24,
fontWeight: FontWeight.w500,
height: 1.5,
),
titleMedium: GoogleFonts.inter(
fontSize: 20,
fontWeight: FontWeight.w500,
height: 1.5,
),
titleSmall: GoogleFonts.inter(
fontSize: 18,
fontWeight: FontWeight.w500,
height: 1.5,
),
bodyLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1.5,
),
bodyMedium: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.5,
),
bodySmall: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w400,
height: 1.5,
),
labelLarge: GoogleFonts.inter(
fontSize: 16,
fontWeight: FontWeight.w500,
height: 1.5,
letterSpacing: 0.5,
),
labelMedium: GoogleFonts.inter(
fontSize: 14,
fontWeight: FontWeight.w500,
height: 1.5,
letterSpacing: 0.5,
),
labelSmall: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w500,
height: 1.5,
letterSpacing: 0.5,
),
caption: GoogleFonts.inter(
fontSize: 12,
fontWeight: FontWeight.w400,
height: 1.5,
),
overline: GoogleFonts.inter(
fontSize: 10,
fontWeight: FontWeight.w500,
height: 1.5,
letterSpacing: 1.5,
),
code: GoogleFonts.robotoMono(
fontSize: 14,
fontWeight: FontWeight.w400,
height: 1.75,
),
);
// More rounded for modern e-commerce feel
final radius = ChromiaRadius(
none: 0,
xs: 4,
s: 8,
m: 16,
l: 20,
xl: 24,
xxl: 32,
full: 9999,
);
return ChromiaThemeData(
colors: colors,
typography: typography,
spacing: ChromiaSpacing.defaultSpacing(),
radius: radius,
shadows: ChromiaShadows.light(),
brightness: Brightness.light,
);
}
static ChromiaThemeData dark() {
final colors = ChromiaColors.fromPrimary(
_brandPrimary,
isDark: true,
).copyWith(
secondary: _brandSecondary,
);
// Use same typography and radius as light theme
final lightTheme = light();
return ChromiaThemeData(
colors: colors,
typography: lightTheme.typography,
spacing: ChromiaSpacing.defaultSpacing(),
radius: lightTheme.radius,
shadows: ChromiaShadows.dark(),
brightness: Brightness.dark,
);
}
}
// Usage in app
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChromiaTheme(
data: ECommerceTheme.light(),
child: MaterialApp(
theme: ECommerceTheme.light().toMaterialTheme(),
darkTheme: ECommerceTheme.dark().toMaterialTheme(),
home: HomePage(),
),
);
}
}
Next Steps
Multi-Brand
Support multiple brands in one app
Design Tokens
Explore all available design tokens
