diff --git a/CHANGELOG.md b/CHANGELOG.md
index f5cd927..fadb58c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,11 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.
## [Unreleased]
+### 🐛 Bug Fixes
+
+- Fixed: flex-1 + justify-between space distribution broken by incorrect Flexible wrapping (#45)
+- Fixed: shrink-0 no longer creates Flexible wrapper — correctly preserves intrinsic size (#45)
+
---
## [1.0.0-alpha.4] - 2026-03-24
diff --git a/doc/layout/flexbox.md b/doc/layout/flexbox.md
index 40f16e2..193b82a 100644
--- a/doc/layout/flexbox.md
+++ b/doc/layout/flexbox.md
@@ -61,7 +61,7 @@ WDiv(
| `items-{alignment}` | `align-items: ...` | `CrossAxisAlignment` |
| `gap-{n}` | `gap: {n}` | `SizedBox` (spacer) |
| `flex-1` | `flex: 1` | `Expanded()` |
-| `shrink-0` | `flex-shrink: 0` | `Flexible(fit: FlexFit.tight)` |
+| `shrink-0` | `flex-shrink: 0` | No wrapper — preserves intrinsic size |
## Flex Direction
@@ -190,7 +190,7 @@ Control how individual children resize to fill available space.
| `flex-grow` | Alias for `flex-1`. |
| `flex-{n}` | Specific flex factor (e.g., `flex-2`). |
| `shrink` | Allow child to shrink if needed (`FlexFit.loose`). |
-| `shrink-0` | Prevent child from shrinking (`FlexFit.tight`). |
+| `shrink-0` | Preserve intrinsic size — no Flexible wrapper, child keeps its natural dimensions. |
| `flex-none` | Do not grow or shrink. |
diff --git a/lib/src/parser/parsers/flexbox_grid_parser.dart b/lib/src/parser/parsers/flexbox_grid_parser.dart
index c9d6600..551d54d 100644
--- a/lib/src/parser/parsers/flexbox_grid_parser.dart
+++ b/lib/src/parser/parsers/flexbox_grid_parser.dart
@@ -109,7 +109,6 @@ class FlexboxGridParser implements WindParserInterface {
/// Maps flex child properties to `FlexFit`
static const _flexFitMap = {
'shrink': FlexFit.loose, // flex-shrink: 1 (can shrink)
- 'shrink-0': FlexFit.tight, // flex-shrink: 0 (don't shrink)
'flex-auto': FlexFit.loose,
'flex-initial': FlexFit.loose,
'flex-shrink': FlexFit.loose,
diff --git a/lib/src/widgets/w_div.dart b/lib/src/widgets/w_div.dart
index e49b7fe..fb37d09 100644
--- a/lib/src/widgets/w_div.dart
+++ b/lib/src/widgets/w_div.dart
@@ -417,6 +417,13 @@ class WDiv extends StatelessWidget {
if (child is WText && _hasFlexClass(child.className)) {
return child;
}
+ // Skip shrink-0 children (should not shrink — keep intrinsic size)
+ if (child is WDiv && _hasShrinkZero(child.className)) {
+ return child;
+ }
+ if (child is WText && _hasShrinkZero(child.className)) {
+ return child;
+ }
return Flexible(child: child);
}).toList()
: gappedChildren;
@@ -432,6 +439,20 @@ class WDiv extends StatelessWidget {
}
}
+ /// Checks if a className contains shrink-0 token that should preserve
+ /// intrinsic size. Uses token-based matching to avoid false positives
+ /// from substring matches (e.g. a hypothetical `no-shrink-0`).
+ /// Matches both bare `shrink-0` and prefixed variants like `md:shrink-0`.
+ static bool _hasShrinkZero(String? className) {
+ if (className == null || className.isEmpty) return false;
+ for (final token in className.split(' ')) {
+ if (token == 'shrink-0' || token.endsWith(':shrink-0')) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/// Checks if a className contains flex-N classes that produce Expanded widgets
static bool _hasFlexClass(String? className) {
if (className == null) return false;
diff --git a/test/parser/parsers/flexbox_grid_parser_test.dart b/test/parser/parsers/flexbox_grid_parser_test.dart
index 15b3a36..25c5700 100644
--- a/test/parser/parsers/flexbox_grid_parser_test.dart
+++ b/test/parser/parsers/flexbox_grid_parser_test.dart
@@ -155,7 +155,7 @@ void main() {
test('parses shrink-0 class', () {
final styles = parser.parse(WindStyle(), ['shrink-0'], context);
- expect(styles.flexFit, FlexFit.tight);
+ expect(styles.flexFit, isNull);
});
test('parses items-baseline class', () {
@@ -167,7 +167,7 @@ void main() {
test('applies last-class-wins for shrink', () {
final styles =
parser.parse(WindStyle(), ['shrink', 'shrink-0'], context);
- expect(styles.flexFit, FlexFit.tight);
+ expect(styles.flexFit, FlexFit.loose);
});
test('returns unchanged styles when classes is null', () {
@@ -183,9 +183,9 @@ void main() {
expect(styles.flexFit, FlexFit.loose);
});
- test('shrink-0 -> FlexFit.tight', () {
+ test('shrink-0 does not set flexFit', () {
final styles = parser.parse(WindStyle(), ['shrink-0'], context);
- expect(styles.flexFit, FlexFit.tight);
+ expect(styles.flexFit, isNull);
});
test(
@@ -197,10 +197,10 @@ void main() {
});
test('last-class-wins override logic', () {
- // Flex shrink overrides
+ // Flex shrink overrides — shrink-0 no longer sets flexFit
expect(
parser.parse(WindStyle(), ['shrink', 'shrink-0'], context).flexFit,
- FlexFit.tight,
+ FlexFit.loose,
);
expect(
parser.parse(WindStyle(), ['shrink-0', 'shrink'], context).flexFit,
diff --git a/test/widgets/w_div/flex_shrink_test.dart b/test/widgets/w_div/flex_shrink_test.dart
index c22fd8e..aed9916 100644
--- a/test/widgets/w_div/flex_shrink_test.dart
+++ b/test/widgets/w_div/flex_shrink_test.dart
@@ -249,6 +249,120 @@ void main() {
},
);
+ testWidgets(
+ 'flex-row justify-between with flex-1 child should not wrap shrink-0 child with Flexible',
+ (tester) async {
+ // Bug: when needsSpaceDistribution is true, ALL non-flex children
+ // get wrapped with Flexible — including shrink-0 children that should
+ // keep their intrinsic size. The shrink-0 child must NOT be Flexible.
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: WindTheme(
+ data: WindThemeData(),
+ child: SizedBox(
+ width: 300,
+ child: const WDiv(
+ className: 'flex flex-row justify-between',
+ children: [
+ WDiv(
+ className: 'flex-1',
+ child: WText('Grows to fill space'),
+ ),
+ WDiv(
+ className: 'shrink-0',
+ child: WText('Fixed size'),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(tester.takeException(), isNull);
+
+ final row = tester.widget(find.byType(Row).first);
+
+ // The shrink-0 child should NOT be wrapped with Flexible.
+ // Only non-flex, non-shrink-0 children may receive Flexible wrapping.
+ // Count Flexible wrappers that are NOT Expanded (i.e. flex:1 children).
+ final flexibleCount = row.children
+ .where((child) => child is Flexible && child is! Expanded)
+ .length;
+
+ expect(
+ flexibleCount,
+ 0,
+ reason:
+ 'shrink-0 child must not be wrapped with Flexible when justify-between is used',
+ );
+ },
+ );
+
+ testWidgets(
+ 'flex-row justify-between with flex-1 child preserves non-flex child intrinsic size',
+ (tester) async {
+ // Bug: the shrink-0 child gets wrapped in Flexible, which forces
+ // Flutter to give it a flex allocation instead of its natural size.
+ // After the fix, the non-flex child must render at its intrinsic width.
+
+ const fixedText = 'Badge';
+
+ await tester.pumpWidget(
+ MaterialApp(
+ home: WindTheme(
+ data: WindThemeData(),
+ child: SizedBox(
+ width: 400,
+ child: const WDiv(
+ className: 'flex flex-row justify-between',
+ children: [
+ WDiv(
+ className: 'flex-1',
+ child: WText('Title that grows'),
+ ),
+ WDiv(
+ className: 'shrink-0 px-2',
+ child: WText(fixedText),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ );
+
+ expect(tester.takeException(), isNull);
+
+ // Verify the shrink-0 child is not forced into equal flex allocation.
+ // If it were wrapped in Flexible(flex: 1) alongside Expanded(flex: 1),
+ // both children would split the 400px container equally → 200px each.
+ // The shrink-0 child should retain its intrinsic width instead.
+ final badgeFinder = find.text(fixedText);
+ expect(badgeFinder, findsOneWidget);
+
+ final badgeRenderBox = tester.renderObject(badgeFinder);
+ final badgeWidth = badgeRenderBox.size.width;
+
+ expect(badgeWidth, greaterThan(0));
+ // Must not equal the 50% split that Flexible(flex:1) would produce
+ expect(
+ badgeWidth,
+ isNot(equals(200.0)),
+ reason:
+ 'shrink-0 child must retain intrinsic size — not equal-flex split',
+ );
+ // Must not fill the entire container
+ expect(
+ badgeWidth,
+ lessThan(400),
+ reason:
+ 'shrink-0 child must not expand to fill entire container width',
+ );
+ },
+ );
+
testWidgets(
'flex-col should NOT auto-wrap children with Flexible',
(tester) async {