Theming¶
Introduction¶
Theming in Bond provides a comprehensive system for creating consistent, beautiful, and maintainable user interfaces. Bond's theming approach goes beyond Flutter's basic ThemeData to provide design tokens, semantic color systems, typography scales, and component variants.
Why Bond Theming¶
Traditional Problems¶
Flutter's default theming often leads to inconsistent designs with hardcoded values scattered throughout the codebase.
Bond's Solution¶
Bond provides semantic naming, design tokens, automatic dark mode support, and accessibility built-in through centralized theme definitions.
Design System Foundation¶
Color System¶
class AppColors {
static const Color primaryLight = Color(0xFF1976D2);
static const Color primaryDark = Color(0xFF90CAF9);
static ColorScheme get lightColorScheme => ColorScheme.light(
primary: primaryLight,
secondary: Color(0xFF388E3C),
surface: Color(0xFFFFFFFF),
background: Color(0xFFFAFAFA),
error: Color(0xFFD32F2F),
);
static ColorScheme get darkColorScheme => ColorScheme.dark(
primary: primaryDark,
secondary: Color(0xFF81C784),
surface: Color(0xFF121212),
background: Color(0xFF000000),
error: Color(0xFFEF5350),
);
}
Typography System¶
class AppTextStyles {
static const String fontFamily = 'Inter';
static const TextStyle headlineLarge = TextStyle(
fontFamily: fontFamily,
fontSize: 32,
fontWeight: FontWeight.w400,
height: 1.25,
);
static const TextStyle bodyLarge = TextStyle(
fontFamily: fontFamily,
fontSize: 16,
fontWeight: FontWeight.w400,
height: 1.50,
letterSpacing: 0.15,
);
}
Spacing System¶
class AppSpacing {
static const double xs = 8;
static const double sm = 12;
static const double md = 16;
static const double lg = 24;
static const double xl = 32;
static const EdgeInsets pageInsets = EdgeInsets.all(lg);
static const EdgeInsets cardInsets = EdgeInsets.all(md);
}
Theme Implementation¶
class AppTheme {
static ThemeData lightTheme = ThemeData(
useMaterial3: true,
colorScheme: AppColors.lightColorScheme,
textTheme: _buildTextTheme(AppColors.lightColorScheme),
appBarTheme: _buildAppBarTheme(AppColors.lightColorScheme),
elevatedButtonTheme: _buildElevatedButtonTheme(AppColors.lightColorScheme),
);
static ThemeData darkTheme = ThemeData(
useMaterial3: true,
colorScheme: AppColors.darkColorScheme,
textTheme: _buildTextTheme(AppColors.darkColorScheme),
appBarTheme: _buildAppBarTheme(AppColors.darkColorScheme),
elevatedButtonTheme: _buildElevatedButtonTheme(AppColors.darkColorScheme),
);
}
Context Extensions¶
extension ThemeContextExtension on BuildContext {
ThemeData get theme => Theme.of(this);
ColorScheme get colors => theme.colorScheme;
TextTheme get textStyles => theme.textTheme;
EdgeInsets get pageInsets => AppSpacing.pageInsets;
EdgeInsets get cardInsets => AppSpacing.cardInsets;
}
Dark Mode Support¶
class ThemeService extends ChangeNotifier {
ThemeMode _themeMode = ThemeMode.system;
ThemeMode get themeMode => _themeMode;
Future<void> setThemeMode(ThemeMode mode) async {
_themeMode = mode;
notifyListeners();
await Cache.put('theme_mode', mode.name);
}
Future<void> toggleTheme() async {
final newMode = _themeMode == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
await setThemeMode(newMode);
}
}
Usage Examples¶
class WelcomeCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Card(
margin: context.cardInsets,
child: Padding(
padding: context.cardInsets,
child: Column(
children: [
Text(
'Welcome',
style: context.textStyles.headlineMedium,
),
SizedBox(height: AppSpacing.sm),
Text(
'Get started with Bond',
style: context.textStyles.bodyMedium?.copyWith(
color: context.colors.onSurfaceVariant,
),
),
],
),
),
);
}
}
Best Practices¶
- ✅ Use semantic color names instead of literal colors
- ✅ Implement both light and dark themes from the start
- ✅ Create design tokens for consistent spacing and sizing
- ✅ Test themes with different screen sizes
- ✅ Consider accessibility and contrast ratios