fluttersdk_wind 1.0.0
fluttersdk_wind: ^1.0.0 copied to clipboard
Tailwind CSS for Flutter. Write className='flex p-4 bg-white dark:bg-gray-800' and Wind renders optimized widget trees with dark mode and responsive breakpoints.
Changelog #
All notable changes to this project will be documented in this file.
This project follows Semantic Versioning 2.0.0.
1.0.0 - 2026-06-09 #
First stable release. wind is utility-first, Tailwind-syntax styling for Flutter; v1.0.0 is a complete rewrite of the 0.0.x line with a fresh API surface, a 19-parser pipeline with cached resolution, dark-mode pairs as a first-class contract, and a contracts-based debug bridge for external tooling. All public APIs follow Semantic Versioning 2.0.0 from this point forward.
Migration from 0.0.x. v1 is not source-compatible with v0. Class names like WText, WDiv, WButton are preserved but constructor signatures, the supported className token set, and theme integration differ throughout. Consumers on 0.0.x rewrite their UI against the v1 API; the full v1 documentation lives at fluttersdk.com/wind.
Added #
- 22 public widgets exported from the single barrel
package:fluttersdk_wind/fluttersdk_wind.dart:- Layout:
WDiv,WSpacer - Structural:
WBreakpoint,WDynamic - Display:
WText,WIcon,WImage,WSvg - Interactive:
WAnchor,WButton - Overlay:
WPopover - Form (raw):
WInput,WSelect<T>,WCheckbox,WDatePicker - Form (FormField wrappers):
WFormInput,WFormSelect<T>,WFormMultiSelect<T>,WFormCheckbox,WFormDatePicker - Utility:
WKeyboardActions,WindAnimationWrapper
- Layout:
- 19 className parsers in a token-routing pipeline: background (color, gradient, image), border + radius, ring, shadow, opacity, padding, margin, sizing, flexbox + grid, position (relative + absolute + insets), order, overflow, aspect-ratio, z-index, text (size / weight / family / tracking / leading / decoration / transform / align / overflow), animation, transition (duration + easing), svg fill/stroke + preserve-colors, debug.
WindThemeDatawith 23 configurable fields:brightness,colors,screens,containers,fontSizes,fontWeights,tracking,leading,borderWidths,borderRadius,fontFamilies,ringWidths,ringOffsets,applyDefaultFontFamily,syncWithSystem,baseSpacingUnit,ringColor,opacities,zIndices,shadows,transitionDurations,transitionCurves,animations.WindThemeControllerexposestoggleTheme(),setTheme(),updateTheme(),resetToSystem(). OptionalonThemeChangedcallback fires only on user-initiated toggles, not system brightness syncs.- State system, three layers. Automatic (
hover:,focus:viaWAnchorpointer + focus listeners), framework-managed (loading:,disabled:,checked:,error:), consumer-passed (states: Set<String>?for any custom string likeselected:). All states funnel into a singleSet<String>on the parser cache key. - Responsive prefixes (
sm:,md:,lg:,xl:,2xl:, customizable viaWindThemeData.screens), dark mode (dark:), and platform prefixes (ios:,android:,macos:,web:,mobile:,windows:,linux:). Stackable freely. - CSS positioning utilities:
relative,absolute,top-*,right-*,bottom-*,left-*,inset-*,inset-x-*,inset-y-*, plus negative variants (-top-*,-inset-*) and arbitrary-px values (top-[24px]). - Child order utilities:
order-0throughorder-12,order-first,order-last,order-none, plus arbitraryorder-[n](including negatives) for reordering flex children without changing source order. - Reverse flex direction:
flex-row-reverseandflex-col-reverseflip the main-axis direction sojustify-startmirrors per CSS semantics. self-*align-self shorthand:self-start,self-end,self-center,self-stretch,self-auto,self-baselineas aliases for thealign-self-*long form, matching Tailwind's canonical class name.- Flex grow / basis tokens:
grow(Tailwind shorthand forflex-grow, i.e.flex: 1),grow-0(no grow), andbasis-1/2/basis-1/3/basis-1/4/basis-full/basis-[Npx]. Basis approximates CSSflex-basis: it sets the child's initial MAIN-axis size (width in a row, height in a column) and ignores grow/shrink interplay. The no-grow / no-shrink resets follow last-class-wins:grow-0cancels an earliergrow/flex-grow/flex-N,shrink-0cancels an earliershrink/flex-shrink, andflex-none(flex: 0 0 auto) cancels both, within the same active class list. - Smart column cross-axis stretch: a
flex flex-colwith no explicititems-*token now stretches eachWDiv,WAnchor(any child), andWButtonchild that does not control its own width to the column width (CSSalign-items: stretchdefault), so a clickable nav row (WAnchor(onTap) > WDiv) fills the column width without an explicititems-stretchorw-full. For aWAnchor: when it wraps aWDiv, the innerWDiv's className decides; when it wraps aWTextor raw widget, it always stretches. Children that self-wrap inExpanded/Flexible(grow,flex-grow,flex-auto,flex-initial,shrink,flex-shrink,flex-N, in any state/breakpoint variant), children with an explicitw-*/min-w-*/max-w-*(includingw-full), absolute children, bareWTextleaves, and raw Flutter widgets are left untouched.shrink-0/flex-nonechildren still stretch on the cross axis (their no-shrink effect is main-axis only, matching CSS). Rows are unaffected. - Inline color props:
WDiv.backgroundColorandWText.foregroundColorfor runtime-dynamic colors. Override anybg-*/text-*from className and stay out of the parser cache key. WBreakpointwidget: declarative per-breakpoint widget tree builder. Reach for it when className prefixes are not enough because the widget structure itself changes between breakpoints.WDynamic: JSON-driven widget tree renderer. 13 Wind types + 16 Flutter core types allowed by default, extensible viabuilders:, restrictable viadenyWidgets:, withcustomIcons:for user-defined icon mappings (24 built-in glyphs). State binding by widgetid, action dispatch viaWActionHandler, max recursion depth 50.- Accessibility / Semantics on 7 interactive widgets (
WAnchor,WButton,WInput,WFormInput,WCheckbox,WSelect,WDatePicker): emitSemanticsnodes with role + label, password fields markobscured. Enables PlaywrightgetByRole/getByLabel/getByTextresolution against the Flutter web build. New optionalsemanticLabelparameters onWInput,WButton, andWAnchor; the button/anchor label gives icon-only controls an accessible name when there is no child text forMergeSemanticsto absorb. Wind.installDebugResolver(): call insidekDebugModeto register aWindDebugResolverImplagainst the newfluttersdk_wind_diagnostics_contractsbridge. Resolves 7 fields per Wind widget element:className,breakpoint,brightness,platform,states, conditionalbgColor, conditionaltextColor. Consumed byfluttersdk_duskfor E2E snapshot capture and by any runtime inspector. Tree-shaken in release builds.wind-uiskill v2.0.2 community pattern:skills/wind-ui/SKILL.mdsection 15 plusskills/wind-ui/references/community.mdadd opt-in star + issue-report CTAs surfaced once per session after a verified Wind task or a genuine wind-side bug. Prose-permission only, never auto-executed,gh auth status-gated. (#89)
Changed #
- BREAKING. Complete API rewrite from 0.0.x. The legacy
lib/src/parsers/(28 modules) andlib/src/components/(8 modules) directories were deleted and reimplemented underlib/src/parser/parsers/andlib/src/widgets/. Class namesWText,WDiv,WButtonare preserved but their constructor signatures, the className token set they accept, and theme integration differ. Not source-compatible with v0. - BREAKING. Flutter SDK minimum raised from
>=3.3.0to>=3.27.0. Dart SDK constraint set to>=3.4.0 <4.0.0. - BREAKING. Parser cache is now always on. The opt-in
trackProvenanceflag onWindParser.parse()and theWindStyle.resolvedViafield are gone; debug-tooling consumers read widget state throughWind.installDebugResolver()and thefluttersdk_wind_diagnostics_contractscontract package instead. - BREAKING. The public
DatePickerModeenum is renamed toWDatePickerMode. The old name collided with Flutter Material's ownDatePickerMode, forcing any consumer importing bothpackage:flutter/material.dartand the wind barrel tohideone symbol.WDatePicker/WFormDatePickermode:now takesWDatePickerMode.
Removed #
- BREAKING.
WindDuskIntegrationclass and thelib/dusk_integration.dartsub-barrel. Replaced byWind.installDebugResolver()from the main barrel. - BREAKING.
fluttersdk_duskas a wind dependency at any level. Consumers needing Dusk for their own E2E add it to their own pubspec. - BREAKING.
google_fontsandplatform_infodependencies. Consumers depending on them transitively must add explicit deps. - BREAKING.
WindParser.parse(trackProvenance:)parameter,WindStyle.resolvedViafield, and theenableProvenance()toggle. The contracts-based diagnostic bridge does not require provenance instrumentation. - BREAKING. 13 internal parser classes (
AspectRatioParser,BackgroundParser,BorderParser,FlexboxGridParser,MarginParser,OpacityParser,OverflowParser,PaddingParser,RingParser,SizingParser,TextParser,TransitionParser,ZIndexParser),WindPlatformService,WindLogger,LogEntry,WDynamicRenderer, andWindDebugResolverImplare no longer exported from the public barrel (package:fluttersdk_wind/fluttersdk_wind.dart). These were always internal implementation details; any consumer referencing them by name must remove those references. The public widget and theme API is unaffected.
Fixed #
WTextbare rendering: aWTextused outside aMaterialApp/Scaffoldnow renders with a brightness-aware baseline color (Colors.whiteon dark platforms,Colors.blackon light, read fromMediaQuery.platformBrightness) instead of Flutter's debug yellow-underline fallback. When noDirectionalityancestor exists,WTextinjects one defaulting toTextDirection.ltr. Explicitly supplied colors (className text-*,foregroundColor,textStyle) still win and are unaffected.- Background image parser (
bg-[/abs/path]): theFileImage(File(...))branch is now guarded bykIsWeb; on web, wheredart:ioFileis unsupported, the image degrades gracefully (skipped) instead of throwing at runtime. Non-web behavior is unchanged.pubspec.yamlnow declares explicit platform support (android,ios,macos,web,linux,windows) so pub.dev platform detection is not narrowed by thedart:ioimport graph. - WASM compatibility: removed
dart:iofrom the library import graph (platform_service.dartnow usesdefaultTargetPlatform; absolute-pathbg-[/...]image resolution moved behind a conditional import). The package is nowis:wasm-ready, raising the pana/pub.dev platform-support score to 20/20 (160/160). (#95) max-w-prose: corrected value from 1040 px (65 × 16, an incorrect approximation) to 512 px, matching the actual parser output. Docs and skill references updated accordingly.WButton/WAnchorsemanticLabel: theSemanticsnode now setsexcludeSemantics: trueand liftsonTap/onLongPressonto itself whensemanticLabelis set, so the label overrides any child text instead of concatenating with it underMergeSemantics, while activation is preserved.Wind.installDebugResolver(): the resolver no longer crashes on a className-less W-widget.WindDebugResolverImpl.resolveguarded its dynamicclassNameread, so a bareWAnchor(orWBreakpoint/WindAnimationWrapper/WKeyboardActions) in the tree no longer throwsNoSuchMethodErrorand abort the entirefluttersdk_dusk/ telescope diagnostic snapshot.WInput:px-*horizontal padding now matches the requested value exactly;OutlineInputBorder.gapPaddingis set to0.0sopx-3produces a 12 px inset instead of 16 px. Multiline geometry unchanged. (#61)WindParser.findAndGroupClasses: duplicate tokens flow through to the parser pipeline so the documented last-class-wins contract holds on repeated overrides liketop-8 top-4 top-8; previously.toSet()dropped the trailing occurrence.WindParser.parse: cache is bypassed in both directions (no read, no write) whenbaseStyleis non-null so per-call styles do not return stale cached entries or poison the cache slot for default-flag callers.example/lib/routes.dart: six widget routes (/widgets/w-input-multiline,/widgets/w-input-search,/widgets/w-popover-alignment,/widgets/w-select_multi,/widgets/w-select_single,/widgets/w-text-transform) renamed to snake_case to match their page-file basenames so live doc iframes atwind.fluttersdk.com/preview/widgets/<key>resolve. Two dead pages (layout/grid_basic,layout/order) without documentation references were removed.BackgroundParser:bg-[#hex]arbitrary-color backgrounds no longer also resolve to a bogusAssetImage("assets/#hex"). The image regex now excludes#-leading bracket values so a hex literal is parsed only as a color, eliminating a stray failed asset fetch on every arbitrary-hex background.flex-none: now means CSSflex: 0 0 auto(no grow AND no shrink). It no longer maps to a shrinkingFlexFit.loose; instead it routes through the same no-shrink path asshrink-0, so aflex-nonechild in ajustify-betweenrow keeps its intrinsic size instead of being forced into aFlexibleshrink allocation.WindStyle.copyWith: a padding-, margin-, or text-only style keepsdecoration == nullinstead of fabricating an emptyBoxDecoration. This stopsWDiv/WTextfrom wrapping a needlessContaineraround non-decorated content.shadow-nonelikewise no longer forces a Container.WDynamicRenderer: malformed JSON degrades gracefully. A non-stringtypeor non-listchildrenis coerced defensively (routed through the whitelist / treated as no children) instead of throwing an implicit-downcastTypeErrorout ofbuild().WindThemeData: implements value-basedoperator ==andhashCode. The equality guards inWindThemeController.setThemeand_WindThemeState.didUpdateWidgetnow compare by value, so a fresh defaultWindThemeData()on a parent rebuild no longer clobbers a priortoggleTheme()choice or triggers spurious full-tree rebuilds.
Quality #
- 1,224 tests across 83 test files; line coverage 90.3% (CI gate enforces
>= 90%via./tool/coverage.sh 90). - New regression coverage in
test/parser/wind_parser_cache_test.dartfor the last-class-wins-on-duplicates andbaseStyle-bypasses-cache contracts. - New regression coverage for the arbitrary-hex background (
background_parser_test.dart),decoration-stays-null contract (wind_style_test.dart), malformed-JSON graceful degradation (w_dynamic_renderer_test.dart),WindThemeDatavalue equality (wind_theme_data_test.dart), and icon-only button Semantics label (w_button_test.dart). tool/coverage.shportable threshold-aware lcov wrapper; GitHub Actions gate fails any PR dropping below 90%.- Surgical
// coverage:ignore-linepragmas only on lines structurally unreachable fromflutter test(kDebugModebranches,dart:ioPlatform.is*branches not matching the CI host). Each pragma carries a one-line WHY comment. - Final 1.0.0 QA gate: added permanent regression suites under
test/pixel/(px-exact geometry + color viagetRect/getSize/renderObject, no golden files),test/interaction/(tap/hover/focus/disabled/responsive/animation/overlay in-process), andtest/performance/(parser cache hit/miss speedup ratio gate, ~26x, plus report-only large-tree pump timing). Validated live via a fresh consumer app driven byfluttersdk_dusk(all routes navigated,wind:enricher 7-field block confirmed, screenshots manually compared). No behavioral regressions found.
Production deps: flutter (SDK), flutter_svg ^2.0.0, fluttersdk_wind_diagnostics_contracts ^1.0.0. Dev deps: flutter_test (SDK), flutter_lints ^5.0.0. Full v1 documentation at fluttersdk.com/wind; LLM-facing skill at skills/wind-ui/ distributed via fluttersdk/ai (npx skills add fluttersdk/ai --skill wind-ui).
Previous versions #
The 1.0.0-alpha.1 through 1.0.0-alpha.10 release notes (Feb 2026 to May 2026) are preserved in git history and on the v0 branch. The 0.0.x line is end-of-life; consumers pin to ^1.0.0 going forward.