Theme Configuration
- Introduction
- The WindTheme Widget
- Theme Change Callbacks
- Resetting to System Theme
- Colors
- Typography
- Spacing and Sizing
- Borders and Shadows
- Customizing Defaults
- Quick Reference
Introduction
Wind is designed to be fully customizable from the ground up. If you've ever used Tailwind CSS, you'll feel right at home with how we handle "design tokens." Instead of hunting through nested widget properties to change a color or a font size, you define your design system once in your theme configuration.
Everything in Wind—from the colors in bg-blue-500 to the spacing in p-4—is driven by WindThemeData. This ensures that your UI remains consistent across the entire application while giving you the freedom to break away from defaults whenever you need to.
The WindTheme Widget
To start customizing your design system, you need to wrap your application with the WindTheme widget. This widget acts as a provider, making your theme configuration available to all W widgets in the tree.
[!IMPORTANT] Always use the
data:parameter when providing your theme configuration. Earlier alpha versions usedtheme:, but this has been updated for consistency.
Let's look at a basic setup:
import 'package:fluttersdk_wind/fluttersdk_wind.dart';
void main() {
runApp(
WindTheme(
data: WindThemeData(
// We'll customize this in the next sections
),
onThemeChanged: (brightness) {
// Optional: persist user's theme preference
},
child: MaterialApp(
home: MyHome(),
),
),
);
}
Theme Change Callbacks
When a user manually toggles the theme via toggleTheme(), you may want to persist their preference. The onThemeChanged callback fires only on user-initiated theme changes—it does not fire when the system brightness changes automatically.
WindTheme(
onThemeChanged: (brightness) {
// Persist user preference
Vault.set('theme_mode', brightness == Brightness.dark ? 'dark' : 'light');
},
data: WindThemeData(),
child: MaterialApp(
home: MyHome(),
),
)
[!IMPORTANT]
onThemeChangedonly fires when the user callstoggleTheme(). Automatic system brightness sync does not trigger this callback.
Resetting to System Theme
After a user manually toggles the theme, the automatic system sync is disabled to respect their choice. To re-enable automatic system brightness sync, call resetToSystem():
// Re-enable system brightness sync
WindTheme.of(context).resetToSystem();
// Or via extension
context.windTheme.resetToSystem();
This immediately syncs the theme with the current platform brightness and re-enables the syncWithSystem flag so future OS changes are reflected automatically.
Colors
Colors in Wind are defined as palettes. When you add a color to your theme, Wind expects a MaterialColor (or a Map of shades) so it can resolve utilities like bg-brand-50 through bg-brand-950.
Here's how to define custom colors:
WindThemeData(
colors: {
// Single color - Wind automatically generates shades
'brand': Colors.indigo,
// Explicit shades for fine-tuned control
'accent': MaterialColor(0xFF3B82F6, {
50: Color(0xFFEFF6FF),
500: Color(0xFF3B82F6),
900: Color(0xFF1E3A8A),
}),
},
)
Once defined, these become available as utility classes immediately: text-brand-600, bg-accent-500/50, or border-brand-900.
Typography
You can take full control of your app's typography by overriding font families, sizes, and weights. Wind applies a "sans" font family globally by default, mimicking Tailwind's behavior.
WindThemeData(
fontFamilies: {
'sans': 'Inter', // The default global font
'display': 'Oswald', // Accessible via font-display
},
fontSizes: {
'xs': 12.0,
'base': 16.0,
'xl': 20.0,
'4xl': 36.0,
},
fontWeights: {
'thin': FontWeight.w100,
'bold': FontWeight.w700,
},
)
But what if you don't want Wind to inject a global DefaultTextStyle? You can simply set applyDefaultFontFamily: false in your configuration.
Spacing and Sizing
Wind uses a numeric scale for spacing (p-4, m-2, gap-6). By default, each unit is equal to 4.0 logical pixels. This means p-4 translates to 16.0 pixels.
If your design system is built on a different grid (like a 5px or 8px grid), you can adjust the baseSpacingUnit:
WindThemeData(
baseSpacingUnit: 5.0, // Now p-4 = 20px
)
You can also customize the "container" sizes or "screen" breakpoints if the default Tailwind values don't fit your needs:
WindThemeData(
screens: {
'sm': 600,
'md': 900,
'lg': 1200,
},
)
Borders and Shadows
The "feel" of your app often comes down to its borders and shadows. Wind allows you to define these scales explicitly so your rounded-lg or shadow-xl classes are always consistent.
WindThemeData(
borderRadius: {
'none': 0,
'sm': 2,
'DEFAULT': 4, // Applied by 'rounded' class
'lg': 8,
'full': 9999,
},
shadows: {
'sm': [BoxShadow(color: Colors.black12, blurRadius: 2)],
'md': [BoxShadow(color: Colors.black26, blurRadius: 6, offset: Offset(0, 2))],
},
)
Customizing Defaults
Wind provides a comprehensive default theme that matches Tailwind CSS v3. When you provide your own WindThemeData, your values are merged with the defaults.
If you want to tweak just one or two things from an existing configuration, use copyWith:
final darkTheme = myDefaultTheme.copyWith(
brightness: Brightness.dark,
ringColor: Colors.amber,
);
Quick Reference
WindThemeData exposes 23 configurable fields. The table below groups them by concern.
Mode and Behavior
| Property | Type | Default | Description |
|---|---|---|---|
brightness |
Brightness |
light |
Initial mode (light or dark) |
syncWithSystem |
bool |
true |
Auto-follow OS brightness until the user calls toggleTheme() |
applyDefaultFontFamily |
bool |
true |
Inject Wind's default font family as a global DefaultTextStyle |
baseSpacingUnit |
double |
4.0 |
Multiplier for numeric spacing (p-4 → 4 * 4 = 16px) |
Tokens
| Property | Type | Description |
|---|---|---|
colors |
Map |
Custom color palettes (bg-brand-500, etc.) |
screens |
Map |
Breakpoint min-widths (sm, md, lg, xl, 2xl, custom) |
containers |
Map |
Container max-widths (container utility) |
fontFamilies |
Map |
Font aliases (sans, serif, mono, custom) |
fontSizes |
Map |
Text size scale (text-xs through text-6xl) |
fontWeights |
Map |
Weight scale (font-thin through font-black) |
tracking |
Map |
Letter spacing (tracking-tight, etc.) |
leading |
Map |
Line height (leading-none, etc.) |
borderWidths |
Map |
Border width scale (border-2, etc.) |
borderRadius |
Map |
Corner radius scale (rounded-lg, etc.) |
shadows |
Map |
Shadow definitions (shadow-md, etc.) |
opacities |
Map |
Opacity scale (opacity-50, etc.) |
zIndices |
Map |
Z-index scale (z-10, etc.) |
transitionDurations |
Map |
Duration scale (duration-200, etc.) |
transitionCurves |
Map |
Easing scale (ease-in-out, etc.) |
animations |
Map |
Animation types (animate-spin, etc.) |
Ring (Focus) Effects
| Property | Type | Default | Description |
|---|---|---|---|
ringColor |
Color |
Tailwind blue-500 | Default ring color when not overridden by ring-{color} |
ringWidths |
Map |
0,1,2,4,8 |
Ring width scale (ring-2, etc.) |
ringOffsets |
Map |
0,1,2,4,8 |
Ring offset scale (ring-offset-2, etc.) |
Widget-level callback
onThemeChanged is a WindTheme widget parameter, not a WindThemeData field. It fires on user-initiated toggleTheme() calls and is documented in the Theme Change Callbacks section above.
For more details on how to sync these values with Flutter's standard Material components, check out the Theme Binding guide.