| name | wind-ui |
| description | Wind UI v1 utility-first styling for Flutter. ALWAYS activate for: WDiv, WText, WButton, WInput, WSelect, WCheckbox, WIcon, WImage, WSvg, WPopover, WAnchor, WFormInput, WFormSelect, WFormMultiSelect, WFormCheckbox, WDatePicker, WFormDatePicker, WSpacer, className, WindTheme, wind-ui, Flutter styling, Flutter layout, mobile UI, app design. Turkish: Flutter tasarım, uygulama arayüzü, mobil UI, Flutter stil, className kullanımı. |
Wind UI v1
Utility-first styling for Flutter UI. Translate Tailwind-like classes to Flutter render tree semantics via className string parsing.
1. Core Laws
- W-prefix mandate: Use
WDiv (not Container), WText (not Text), WButton (not ElevatedButton/GestureDetector).
- className-first: ALL styling via
className string. Never inline BoxDecoration, TextStyle, or EdgeInsets when a Wind equivalent exists.
- dark: is mandatory: Every
bg-, text-, and border- class MUST have a dark: counterpart.
- Trailing commas: Always on the last constructor parameter and list item.
- Multi-line: Constructor parameters must always be multi-line when 3 or more.
- No theory output: When applying styling, do not explain the classes — just provide the code.
2. Flutter Layout Reality
Flutter constraint resolution differs fundamentally from CSS. Wind UI maps className syntax to native Flutter semantics. Follow these rules to avoid overflows and unbounded height errors.
Space-filling
| Goal | Use | Why |
|---|
| Fill remaining space in Row/Column | flex-1 | Maps to Expanded — negotiates available space |
| Full width in Column context | w-full | SizedBox(width: double.infinity) — works here |
| Full width in Row context | flex-1 (NOT w-full!) | w-full causes RenderFlex overflow in Rows |
flex-none / shrink-0 | No flex shrink — unlike CSS, Flutter Row/Column children DON'T auto-shrink; use flex-1 to allow growth | Row child stays fixed width |
Scrollable layouts
| Goal | className | Required context |
|---|
| Vertical scroll | overflow-y-auto + scrollPrimary: true | Parent MUST have bounded height |
| Bounded height for scroll | flex-1 on the scrollable WDiv | Inside a Column/Flex parent |
| Horizontal scroll | overflow-x-auto | Same rules |
WDiv Composition Rules
| className contains | Flutter widget | Notes |
|---|
flex flex-col | Column | flex alone defaults to Row |
flex flex-row or flex | Row | Direction default |
wrap or grid | Wrap | NOT flex-wrap (no-op!) |
overflow-y-auto | SingleChildScrollView | Needs bounded height |
relative | Stack(clipBehavior: Clip.none) | Children split: normal layout + Positioned |
absolute top-4 right-4 | Positioned(top: 16, right: 16) | Must be inside a relative parent |
absolute inset-0 | Positioned(top: 0, right: 0, bottom: 0, left: 0) | Full overlay pattern |
hidden | SizedBox.shrink() | |
Critical Layout Gotchas:
// ❌ WRONG — overflow in Row
WDiv(
className: 'flex flex-row',
children: [
WDiv(className: 'w-full', child: WText('Long text')), // OVERFLOW
],
)
// ✅ CORRECT
WDiv(
className: 'flex flex-row',
children: [
WDiv(className: 'flex-1', child: WText('Long text')), // Fills remaining space
],
)
// ❌ WRONG — unbounded height scroll
Column(
children: [
WDiv(className: 'overflow-y-auto', children: [...]) // ERROR: infinite height
],
)
// ✅ CORRECT
WDiv(
className: 'flex flex-col h-full', // Bounded
children: [
WDiv(
className: 'flex-1 overflow-y-auto',
scrollPrimary: true, // iOS tap-to-top
children: [...],
),
],
)
// ❌ WRONG — truncate without bounded width
WDiv(
className: 'flex flex-row',
children: [
WText('Very long title', className: 'truncate'), // OVERFLOW
],
)
// ✅ CORRECT
WDiv(
className: 'flex flex-row',
children: [
WDiv(
className: 'flex-1', // Bounds the text
child: WText('Very long title', className: 'truncate'),
),
],
)
// ❌ WRONG — absolute without relative parent
WDiv(
className: 'flex flex-row',
children: [
WDiv(className: 'absolute top-0 right-0', child: badge), // No Stack!
],
)
// ✅ CORRECT — relative parent creates Stack
WDiv(
className: 'relative flex flex-row',
children: [
WDiv(className: 'flex-1', child: content),
WDiv(className: 'absolute top-0 right-0', child: badge), // Positioned overlay
],
)
Note: h-full inside a scrollable parent results in an infinite height error. Use min-h-screen instead. Native Flutter widgets (e.g., ListView.builder, charts) inside a Row or Column MUST be wrapped in a manual Expanded(). absolute children only work inside a relative parent — only WDiv and WText are detected as absolute; wrap other widgets in a WDiv.
3. Widget Quick Reference
| Widget | Required Props | Key Optional Props | Use For |
|---|
WDiv | - | className, child/children, states, scrollPrimary | Any container, layout |
WText | data | className, selectable, states | All text rendering |
WButton | child | onTap, isLoading, disabled, className | All interactive buttons |
WInput | value, onChanged | type, placeholder, className, placeholderClassName | Standalone inputs |
WSelect | options, value/values, onChange | searchable, isMulti, onCreateOption, className | Dropdowns |
WCheckbox | value, onChanged | className | Toggle checkboxes |
WIcon | icon | className | Icons with styling |
WImage | src | className | Images with object-fit |
WSvg | src | className, preserve-colors | SVG with fill/stroke/preserve |
WPopover | triggerBuilder, contentBuilder | alignment, className | Dropdowns, menus, tooltips |
WAnchor | child | onTap, className | Raw interactive wrapper |
WFormInput | (FormField) | controller, type, placeholder, className, validator | MagicForm-integrated input |
WFormSelect | (FormField) | options, searchable, className | MagicForm-integrated select |
WFormMultiSelect | (FormField) | options, onCreateOption, className | MagicForm multi-select |
WFormCheckbox | (FormField) | label, className | MagicForm-integrated checkbox |
WDatePicker | mode, onDateSelected/onRangeSelected | className, initialDate, firstDate, lastDate | Single date or date range picker |
WFormDatePicker | (FormField) | mode, className, firstDate, lastDate | MagicForm-integrated date picker |
WSpacer | - | className | Semantic spacing between siblings |
WDynamic | json | actions, controller, customIcons, builders, denyWidgets | Server-driven UI from JSON |
Rules:
child and children are mutually exclusive on WDiv.
WDiv auto-wraps in WAnchor if hover:, focus:, or active: is present in the className.
WButton with isLoading: true activates loading: prefix classes and disables interaction.
WSelect with isMulti: true must use values + onMultiChange instead of value + onChange.
WSpacer is a const-friendly semantic spacer — prefer over SizedBox or empty WDiv for gaps between siblings.
WDatePicker mode: DatePickerMode.single or DatePickerMode.range. Range mode uses onRangeSelected callback.
4. Token System
Spacing Formula: n × 4px (e.g., p-4 = 16px, gap-6 = 24px, m-2 = 8px)
Color Syntax: {role}-{color}-{shade} (e.g., bg-blue-500, text-gray-900, border-red-300)
- Opacity:
bg-primary/50, text-gray-500/75
- Arbitrary:
bg-[#FF5733], w-[200px], text-[18px]
Responsive: Prefix with breakpoint (e.g., md:flex-row, lg:p-6, xl:hidden)
- Breakpoints:
sm (640px), md (768px), lg (1024px), xl (1280px), 2xl (1536px)
Typography Sizes: xs (12), sm (14), base (16), lg (18), xl (20), 2xl (24), 3xl (30), 4xl (36)
Border Radius: rounded (4), rounded-md (6), rounded-lg (8), rounded-xl (12), rounded-2xl (16), rounded-full (9999)
Position Types: relative (renders Stack), absolute (renders Positioned inside a Stack)
Offsets (spacing scale): top-{n} right-{n} bottom-{n} left-{n} (e.g., top-4 = 16px)
Insets: inset-{n} (all sides), inset-x-{n} (left+right), inset-y-{n} (top+bottom)
Negative offsets: -top-{n}, -inset-{n} (prefix with -)
Arbitrary offsets: top-[24px], left-[24px] (px only; % is unsupported)
Note: fixed and sticky are not implemented. Absolute children must be inside a relative parent.
5. State & Modifier Prefixes
| Prefix | Trigger | Example |
|---|
dark: | Dark mode (MANDATORY on all colors) | bg-white dark:bg-gray-800 |
hover: | Mouse hover (via WAnchor) | hover:bg-gray-100 |
focus: | Keyboard focus | focus:ring-2 focus:ring-primary |
disabled: | Widget disabled prop | disabled:opacity-50 |
loading: | WButton isLoading=true | loading:opacity-75 |
active: | Tap down / WAnchor active | active:scale-95 |
selected: | Custom via states Set | selected:bg-primary/10 |
error: | Form validation fail | error:border-red-500 |
sm: md: lg: xl: 2xl: | Screen width breakpoints | md:flex-row |
ios: android: web: mobile: | Platform | ios:pt-12 |