Chromia UI provides built-in support for multi-brand applications, allowing you to maintain different visual identities within the same codebase.
BrandConfig
The BrandConfig class defines brand-specific styling that can be used to generate complete themes.
Constructor
const BrandConfig ({
required String name,
required Color primaryColor,
Color ? secondaryColor,
String ? logoPath,
String ? fontFamily,
String ? monospaceFontFamily,
Map < String , Color > ? customColors,
})
Properties
The name of the brand (e.g., “Acme Corp”, “Brand A”)
The primary brand color used throughout the theme
Optional secondary brand color. If not provided, a variant of the primary color will be used.
Path to the brand’s logo asset
Custom font family for the brand
Custom monospace font family for code and monospaced text
Additional custom colors specific to this brand
Creating a Brand
Define your brand configurations:
import 'package:chromia_ui/chromia_ui.dart' ;
import 'package:flutter/material.dart' ;
class AppBrands {
static const acmeCorp = BrandConfig (
name : 'Acme Corp' ,
primaryColor : Color ( 0xFF2196F3 ),
secondaryColor : Color ( 0xFF03DAC6 ),
logoPath : 'assets/logos/acme.png' ,
fontFamily : 'Roboto' ,
customColors : {
'accent' : Color ( 0xFFFF5722 ),
'highlight' : Color ( 0xFFFFC107 ),
},
);
static const starlightInc = BrandConfig (
name : 'Starlight Inc' ,
primaryColor : Color ( 0xFF9C27B0 ),
secondaryColor : Color ( 0xFFE91E63 ),
logoPath : 'assets/logos/starlight.png' ,
fontFamily : 'Inter' ,
customColors : {
'accent' : Color ( 0xFF673AB7 ),
'highlight' : Color ( 0xFFF50057 ),
},
);
static const techVentures = BrandConfig (
name : 'Tech Ventures' ,
primaryColor : Color ( 0xFF4CAF50 ),
secondaryColor : Color ( 0xFF8BC34A ),
logoPath : 'assets/logos/techventures.png' ,
fontFamily : 'Poppins' ,
);
}
Creating Themes from Brands
Use ChromiaThemeData.fromBrand() to generate a complete theme from a brand config:
// Light theme from brand
final acmeLightTheme = ChromiaThemeData . fromBrand (
AppBrands .acmeCorp,
isDark : false ,
);
// Dark theme from brand
final acmeDarkTheme = ChromiaThemeData . fromBrand (
AppBrands .acmeCorp,
isDark : true ,
);
The fromBrand factory automatically:
Generates a color scheme from the primary color
Applies the custom font family to all text styles
Configures the monospace font for code
Preserves the brand config for later access
Switching Brands at Runtime
Implement brand switching in your application:
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 > {
BrandConfig _currentBrand = AppBrands .acmeCorp;
bool _isDarkMode = false ;
void switchBrand ( BrandConfig newBrand) {
setState (() {
_currentBrand = newBrand;
});
}
void toggleDarkMode () {
setState (() {
_isDarkMode = ! _isDarkMode;
});
}
@override
Widget build ( BuildContext context) {
final theme = ChromiaThemeData . fromBrand (
_currentBrand,
isDark : _isDarkMode,
);
return ChromiaTheme (
data : theme,
child : MaterialApp (
theme : theme. toMaterialTheme (),
home : HomePage (
onBrandChange : switchBrand,
onThemeToggle : toggleDarkMode,
),
),
);
}
}
class HomePage extends StatelessWidget {
final Function ( BrandConfig ) onBrandChange;
final VoidCallback onThemeToggle;
const HomePage ({
required this .onBrandChange,
required this .onThemeToggle,
});
@override
Widget build ( BuildContext context) {
final colors = context.chromiaColors;
final spacing = context.chromiaSpacing;
final brandConfig = context.chromiaTheme.brandConfig;
return Scaffold (
backgroundColor : colors.background,
appBar : AppBar (
title : Text (brandConfig ? .name ?? 'App' ),
actions : [
IconButton (
icon : Icon (context.isDarkTheme ? Icons .light_mode : Icons .dark_mode),
onPressed : onThemeToggle,
),
PopupMenuButton < BrandConfig >(
icon : Icon ( Icons .palette),
onSelected : onBrandChange,
itemBuilder : (context) => [
PopupMenuItem (
value : AppBrands .acmeCorp,
child : Text ( 'Acme Corp' ),
),
PopupMenuItem (
value : AppBrands .starlightInc,
child : Text ( 'Starlight Inc' ),
),
PopupMenuItem (
value : AppBrands .techVentures,
child : Text ( 'Tech Ventures' ),
),
],
),
],
),
body : Padding (
padding : spacing.paddingL,
child : Column (
crossAxisAlignment : CrossAxisAlignment .start,
children : [
if (brandConfig ? .logoPath != null )
Image . asset (
brandConfig ! .logoPath ! ,
height : 60 ,
),
spacing.gapVL,
Text (
'Welcome to ${ brandConfig ?. name ?? "our app" } ' ,
style : context.chromiaTypography.headlineMedium. copyWith (
color : colors.textPrimary,
),
),
],
),
),
);
}
}
Accessing Brand Configuration
Access the current brand config from the theme:
class BrandedWidget extends StatelessWidget {
@override
Widget build ( BuildContext context) {
final theme = context.chromiaTheme;
final brandConfig = theme.brandConfig;
if (brandConfig == null ) {
return Text ( 'No brand configured' );
}
return Column (
children : [
Text ( 'Brand: ${ brandConfig . name } ' ),
if (brandConfig.logoPath != null )
Image . asset (brandConfig.logoPath ! ),
Container (
color : brandConfig.primaryColor,
child : Text ( 'Primary Color' ),
),
],
);
}
}
Custom Brand Colors
Access custom colors defined in the brand config:
class CustomColorWidget extends StatelessWidget {
@override
Widget build ( BuildContext context) {
final brandConfig = context.chromiaTheme.brandConfig;
// Get custom color by key
final accentColor = brandConfig ? . getCustomColor ( 'accent' );
final highlightColor = brandConfig ? . getCustomColor ( 'highlight' );
return Container (
decoration : BoxDecoration (
color : accentColor ?? Colors .grey,
border : Border . all (
color : highlightColor ?? Colors .grey,
width : 2 ,
),
),
child : Text ( 'Custom branded element' ),
);
}
}
Predefined Brand Configurations
Chromia UI includes some predefined brand configurations for testing:
import 'package:chromia_ui/chromia_ui.dart' ;
// Default Chromia brand
final chromiaTheme = ChromiaThemeData . fromBrand ( BrandConfigs .chromia);
// Blue theme
final blueTheme = ChromiaThemeData . fromBrand ( BrandConfigs .blueTheme);
// Green theme
final greenTheme = ChromiaThemeData . fromBrand ( BrandConfigs .greenTheme);
// Purple theme
final purpleTheme = ChromiaThemeData . fromBrand ( BrandConfigs .purpleTheme);
// Orange theme
final orangeTheme = ChromiaThemeData . fromBrand ( BrandConfigs .orangeTheme);
// Red theme
final redTheme = ChromiaThemeData . fromBrand ( BrandConfigs .redTheme);
// Get all predefined brands
final allBrands = BrandConfigs .all;
Brand-Specific Components
Create components that adapt based on the current brand:
class BrandedButton extends StatelessWidget {
final String label;
final VoidCallback onPressed;
const BrandedButton ({
required this .label,
required this .onPressed,
});
@override
Widget build ( BuildContext context) {
final brandConfig = context.chromiaTheme.brandConfig;
final colors = context.chromiaColors;
final spacing = context.chromiaSpacing;
final radius = context.chromiaRadius;
// Different button styles based on brand
final bool isAcmeBrand = brandConfig ? .name == 'Acme Corp' ;
return ElevatedButton (
onPressed : onPressed,
style : ElevatedButton . styleFrom (
backgroundColor : colors.primary,
foregroundColor : colors.onPrimary,
padding : spacing. symmetric (
horizontal : isAcmeBrand ? spacing.xl : spacing.l,
vertical : spacing.m,
),
shape : RoundedRectangleBorder (
borderRadius : isAcmeBrand
? radius.radiusL
: radius.radiusM,
),
),
child : Text (label),
);
}
}
Persisting Brand Selection
Save and restore the user’s brand choice:
import 'package:shared_preferences/shared_preferences.dart' ;
class BrandPreferences {
static const String _keyBrandName = 'selectedBrand' ;
static Future < String ?> getSavedBrand () async {
final prefs = await SharedPreferences . getInstance ();
return prefs. getString (_keyBrandName);
}
static Future < void > saveBrand ( String brandName) async {
final prefs = await SharedPreferences . getInstance ();
await prefs. setString (_keyBrandName, brandName);
}
}
class BrandNotifier extends ChangeNotifier {
BrandConfig _currentBrand = AppBrands .acmeCorp;
BrandNotifier () {
_loadSavedBrand ();
}
Future < void > _loadSavedBrand () async {
final savedBrandName = await BrandPreferences . getSavedBrand ();
if (savedBrandName != null ) {
_currentBrand = _getBrandByName (savedBrandName);
notifyListeners ();
}
}
BrandConfig get currentBrand => _currentBrand;
Future < void > setBrand ( BrandConfig brand) async {
_currentBrand = brand;
await BrandPreferences . saveBrand (brand.name);
notifyListeners ();
}
BrandConfig _getBrandByName ( String name) {
// Map brand names to BrandConfig instances
switch (name) {
case 'Acme Corp' :
return AppBrands .acmeCorp;
case 'Starlight Inc' :
return AppBrands .starlightInc;
case 'Tech Ventures' :
return AppBrands .techVentures;
default :
return AppBrands .acmeCorp;
}
}
}
White-Label Applications
For white-label applications, load brand configs from a remote source:
class BrandService {
static Future < BrandConfig > fetchBrandConfig ( String tenantId) async {
// Fetch brand configuration from API
final response = await http. get (
Uri . parse ( 'https://api.example.com/brands/ $ tenantId ' ),
);
if (response.statusCode == 200 ) {
final data = json. decode (response.body);
return BrandConfig (
name : data[ 'name' ],
primaryColor : Color ( int . parse (data[ 'primaryColor' ]. substring ( 1 ), radix : 16 ) + 0xFF000000 ),
secondaryColor : data[ 'secondaryColor' ] != null
? Color ( int . parse (data[ 'secondaryColor' ]. substring ( 1 ), radix : 16 ) + 0xFF000000 )
: null ,
logoPath : data[ 'logoUrl' ],
fontFamily : data[ 'fontFamily' ],
);
}
throw Exception ( 'Failed to load brand config' );
}
}
class MyApp extends StatefulWidget {
final String tenantId;
const MyApp ({ required this .tenantId});
@override
State < MyApp > createState () => _MyAppState ();
}
class _MyAppState extends State < MyApp > {
BrandConfig ? _brandConfig;
bool _isLoading = true ;
@override
void initState () {
super . initState ();
_loadBrand ();
}
Future < void > _loadBrand () async {
try {
final brand = await BrandService . fetchBrandConfig (widget.tenantId);
setState (() {
_brandConfig = brand;
_isLoading = false ;
});
} catch (e) {
setState (() {
_isLoading = false ;
});
}
}
@override
Widget build ( BuildContext context) {
if (_isLoading) {
return MaterialApp (
home : Scaffold (
body : Center (child : CircularProgressIndicator ()),
),
);
}
final theme = _brandConfig != null
? ChromiaThemeData . fromBrand (_brandConfig ! )
: ChromiaThemeData . light ();
return ChromiaTheme (
data : theme,
child : MaterialApp (
theme : theme. toMaterialTheme (),
home : HomePage (),
),
);
}
}
Best Practices
Consistent brand properties
Ensure all brands define the same set of custom colors and properties for consistent component behavior.
Test your application with all brand configurations to ensure components work correctly with different color schemes.
Always provide fallback values when accessing brand-specific properties: final accentColor = brandConfig ? . getCustomColor ( 'accent' ) ?? colors.primary;
Validate brand configurations to ensure required assets exist: void validateBrand ( BrandConfig brand) {
assert (brand.name.isNotEmpty, 'Brand name is required' );
assert (brand.primaryColor != null , 'Primary color is required' );
// Add more validations as needed
}
Next Steps
Custom Themes Learn more about theme customization
Design Tokens Explore available design tokens