Utility-First
Utility-first is a design methodology built on small, single-purpose classes that compose into complex designs. Wind brings this approach to Flutter: instead of writing custom styles for every component, you build interfaces directly from utility classes in a className.
import 'package:flutter/material.dart';
import 'package:fluttersdk_wind/fluttersdk_wind.dart';
class UtilityFirstWind extends StatelessWidget {
const UtilityFirstWind({super.key});
@override
Widget build(BuildContext context) {
return WCard(
className: 'shadow-lg rounded-lg m-4 p-4 bg-black',
child: WFlex(
className: 'flex-col axis-min gap-2 items-start',
children: [
WText('12', className: 'text-4xl leading-6 font-bold text-white'),
WText('Active users on the website', className: 'text-white leading-4'),
WText(
'Composed entirely from utility classes.',
className: 'text-gray-300 text-xs',
),
],
),
);
}
}
Basic Usage
Every visual decision is expressed as a utility class in className. Spacing, color, typography, and radius all live in one place, so you read the styling without jumping between widget constructors:
WCard(
className: 'shadow-lg rounded-lg p-4 bg-black',
child: WText('Styled in one line.', className: 'text-white text-lg font-bold'),
);
The Flutter Way
Plain Flutter spreads styling across nested constructors and TextStyle objects. The same card requires explicit Card, Padding, Column, and per-Text styles:
import 'package:flutter/material.dart';
class UtilityFirstFlutter extends StatelessWidget {
const UtilityFirstFlutter({super.key});
@override
Widget build(BuildContext context) {
return Card(
margin: const EdgeInsets.all(16),
color: Colors.black,
elevation: 8,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
const Text(
'12',
style: TextStyle(
fontSize: 36,
fontWeight: FontWeight.bold,
color: Colors.white,
height: 1.5,
),
),
const SizedBox(height: 4),
const Text(
'Active users on the website',
style: TextStyle(fontSize: 14, color: Colors.white),
),
],
),
),
);
}
}
The Wind Way
Wind collapses that structure into utility classes on Wind widgets. The intent reads in a single glance and stays consistent because the same tokens map to the same theme values everywhere:
WCard(
className: 'shadow-lg rounded-lg m-4 p-4 bg-black',
child: WFlex(
className: 'flex-col axis-min gap-2 items-start',
children: [
WText('12', className: 'text-4xl leading-6 font-bold text-white'),
WText('Active users on the website', className: 'text-white leading-4'),
],
),
);
Why Utility-First
- Consistency. Tokens resolve to theme values (
bg-black,text-4xl,rounded-lg), so every screen pulls from the same scale instead of one-off magic numbers. - Speed. You style in place without defining a custom widget or
TextStylefor each variation. - Readability. The full visual definition lives on one line next to the structure it styles.
- Adaptivity. The same tokens compose with
dark:, breakpoint, and state prefixes, so one class string covers multiple contexts.
Best Practices
- Reach for utility classes first; extract a custom widget only when a composition repeats across the app.
- Group related tokens in a readable order (layout, spacing, color, typography) inside the
className. - Lean on theme tokens (
bg-gray-200,text-lg) over arbitrary values so dark-mode inversion and theming stay coherent. - Pair color tokens with their
dark:counterparts where a value should not rely on automatic inversion.
Related Documentation
- Dark Mode — how utility colors invert automatically.
- Responsive Design — composing utilities with breakpoint prefixes.
- State-Based Styling — utilities that react to widget state.
- WCard — the card widget used in these examples.