Upgrade Guide
What changed between wind_kit versions and what to edit in your app when you upgrade.
On this page
Overview#
Wind follows Semantic Versioning 2.0.0. A project pinned to ^1.0.0 resolves to the latest 1.x automatically. Within a major version no public API is removed; a minor release can still change behaviour, flagged BREAKING (behaviour) in the changelog. This guide lists the per-version changes worth knowing about, newest first. If a section has no "Action required" entry, the upgrade is drop-in.
Moving to wind_kit#
The package was renamed to wind_kit. To move an app over, replace the previous dependency name in pubspec.yaml with wind_kit, and replace the previous import with the new barrel:
import 'package:wind_kit/wind_kit.dart';
No W* or Wind* class changes, and every className token behaves as before. WInput's selection handles and toolbar are now drawn by the package: lollipop handles, and a toolbar that scrolls sideways when its actions do not fit and has no arrow. Do not depend on both packages in one app: each has its own WindTheme and WindDebugRegistry, so a WDiv from one finds no theme from the other.
Moving to material_ui#
Flutter 3.50 deprecates package:flutter/material.dart and package:flutter/cupertino.dart, and wind_kit builds on material_ui, the package that replaces the Material barrel. The floor is now Dart 3.13 and Flutter 3.47, and an app on Flutter 3.47 to 3.49 can already make the move.
Action required for an app that imports package:flutter/material.dart. Add material_ui to the dependencies and replace the import:
flutter pub add material_ui
// Before
import 'package:flutter/material.dart';
// After
import 'package:material_ui/material_ui.dart';
dart fix --apply --code=migrate_design_widgets performs the import change for you, as the material_ui README describes. It also rewrites package:flutter/cupertino.dart to package:cupertino_ui/cupertino_ui.dart, so an app that uses Cupertino types directly adds cupertino_ui too (flutter pub add cupertino_ui); cupertino_ui types and the framework's Cupertino types are distinct to Dart, so do not mix the two. material_ui defines its own MaterialApp, ThemeData and localisations, so an app that keeps importing package:flutter/material.dart for its MaterialApp hits two symptoms:
- Wind input widgets throw "No MaterialLocalizations found".
WindThemeData.toThemeData()returns aThemeDatathat does not match theThemeDatatype the oldMaterialAppexpects.
The fix leaves export directives alone in the reported Dart 3.13.5 cases (dart-lang/sdk#64560, still open when this guide was written), so a barrel file that re-exports package:flutter/material.dart or package:flutter/cupertino.dart needs its directive replaced by hand with the material_ui or cupertino_ui barrel. Search for export 'package:flutter/material.dart' after running it.
Action required for an app that uses the flutter_localizations delegates. Replace GlobalMaterialLocalizations and GlobalCupertinoLocalizations from flutter_localizations with the delegates from material_ui, and remove the package:flutter_localizations/flutter_localizations.dart import from the same file: both libraries export a GlobalMaterialLocalizations, so keeping both makes the name ambiguous and the build fails. GlobalMaterialLocalizations.delegates already holds the Cupertino, Material and Widgets delegates, so delete separate GlobalWidgetsLocalizations.delegate, GlobalMaterialLocalizations.delegate and GlobalCupertinoLocalizations.delegate entries instead of listing them again. Keep your app's own delegates in the list:
// Before: the delegates of GlobalMaterialLocalizations and GlobalCupertinoLocalizations
// from package:flutter_localizations
// After: the delegates from package:material_ui, next to your own
MaterialApp(
localizationsDelegates: [
AppLocalizations.delegate, // Keep your app's own delegates.
...GlobalMaterialLocalizations.delegates,
],
)
If a third-party package or a subtree still imports package:flutter/material.dart, material_ui ships MaterialUiCompatibilityBridge, which bridges ThemeData and MaterialLocalizations for that subtree; see Step 3 of the material_ui README. material_ui marks it deprecated as a temporary migration utility that a future release removes, so treat it as a stopgap until that subtree moves to material_ui too.
1.8.x to 1.9.0#
A minor release that changes behaviour, flagged BREAKING (behaviour) in the changelog, so a ^1.8.0 constraint picks it up on the next pub get.
tracking-* is now em, as in Tailwind. Letter spacing scales with the font size, so every tracking-* class renders differently.
Action required for a custom WindThemeData.tracking map. It is now read as em multipliers: a value written as pixels now means that many em, so convert it.
// Before: logical pixels
WindThemeData(tracking: {'tight': -1.0})
// After: a multiple of the font size (-1px on 14px text is about -0.07)
WindThemeData(tracking: {'tight': -0.07})
The built-in scale already holds Tailwind's values, so an app that uses only the default tracking-* classes needs no edit; check the few places where the tighter or wider spacing shows.
An arbitrary tracking-[Nem] is now em too (it was read as N pixels), and a negative arbitrary value now applies instead of being dropped.
1.3.x to 1.4.0#
A minor release that changes behaviour, flagged BREAKING (behaviour) in the changelog.
A single-line WInput defaults its Return key to TextInputAction.done instead of .next. Multiline still defaults to .newline, and an explicit textInputAction still wins. Flutter implements .next as a move to the next focusable widget, not the next text field, so the old default could land on a button or a swatch. A form that relied on the old default gets it back per field:
WInput(
textInputAction: TextInputAction.next,
)
1.0.x to 1.1.0#
A backward-compatible minor release. The headline additions are opt-in, so existing apps compile and render unchanged. One debug-only assertion and a few input behaviour changes are worth a quick scan.
New, opt-in#
These features do nothing until you use them.
className aliases (WindThemeData.aliases). Map a bare token to a full utility string and Wind expands it (recursively) before parsing, in every widget:
WindTheme(
data: WindThemeData(
aliases: {
'row': 'flex flex-row',
'col': 'flex flex-col',
'center': 'items-center justify-center',
'card': 'p-4 rounded-lg bg-white dark:bg-gray-800',
},
),
child: const MyApp(),
)
// then anywhere:
WDiv(className: 'row center', children: [...])
See Theming: Aliases for the full semantics (bare-token keys, recursion, shadowing, the empty default).
WIcon.foregroundColor. Pass a runtime Color directly instead of interpolating it into a text-[#hex] className, which would bloat the parser cache:
// category.color is a runtime value (for example from the database)
WIcon(Icons.star, foregroundColor: category.color, className: 'text-lg')
It overrides any text-* / dark:text-* from the className and stays out of the parser cache key, matching WText.foregroundColor.
WInput read-only state. readOnly: true now activates a readonly state, so readonly: prefixed classes style a read-only field the way disabled: styles a disabled one:
WInput(
readOnly: true,
value: 'Locked',
className: 'p-3 rounded-lg bg-white dark:bg-gray-800 readonly:bg-gray-100 dark:readonly:bg-gray-700',
)
Behavior changes to review#
No code change is required, but the rendered result differs in these cases.
WInputrenders Material-free. It now builds onEditableTextinside a decoratedContainerinstead of a MaterialTextField, so it no longer requires aMaterialAppancestor and works under Cupertino, a custom root, or a bareWidgetsApp. If you relied on ambient MaterialInputDecorationtheming bleeding intoWInput, style it withclassNameinstead.- Selection handles are Cupertino-style on every platform. Previously Material-style on Android and web. The copy / cut / paste menu reads
WidgetsLocalizations, so it works under any ancestor. Behaviour is identical; only the handle and toolbar appearance changes on Android and web. InputType.numberrestricts input to a signed decimal on every platform. A default formatter now allows digits, one decimal point, and an optional leading minus, so the field is numeric on web too (where the keyboard type alone enforces nothing). Supply your owninputFormattersto override the default.
Action required#
- Do not pass both
valueandcontrollertoWInput. Supplying both was always a logic error (the controller silently won); it now throws anAssertionErrorin debug builds. Pick one: usevalue+onChangedfor React-style binding, or acontrollerfor imperative control.
// Before (silently used the controller, ignored value)
WInput(value: _text, controller: _controller)
// After: choose one
WInput(value: _text, onChanged: (v) => setState(() => _text = v))
// or
WInput(controller: _controller)
Fixes you get for free#
No action needed; these land automatically.
- Form widget labels, hints, and errors (
WFormInput,WFormSelect,WFormCheckbox,WFormDatePicker) carrydark:pairs, so they stay legible in dark mode. - A conditional
prefix/suffix(for example a clear button that appears once the field has text) no longer drops focus on the first keystroke, and an appearing suffix no longer changes the field height. - A disabled
WInput(enabled: false) is fully non-interactive again and reportsisEnabled: falseto assistive technology. WInputemits a single cleantextFieldsemantics node instead of the previous double node.