single_axis_wrap 1.0.4 copy "single_axis_wrap: ^1.0.4" to clipboard
single_axis_wrap: ^1.0.4 copied to clipboard

A Flutter widget that chooses one complete row or column based on available space.

single_axis_wrap #

Pub Stars License Platform

A Flutter layout widget that attempts to place all children in a single primary axis, falling back to the opposite axis if they exceed the available space.

Unlike Flutter's built-in Wrap (which breaks children into multiple runs) or OverflowBar (which is strictly horizontal-first), SingleAxisWrap commits to exactly one axis for all children and supports both horizontal-first and vertical-first layouts.

SingleAxisWrap demo

Features #

  • Chooses one complete row or one complete column from the available space.
  • Supports both horizontal-first and vertical-first layouts.
  • Provides separate main-axis and cross-axis alignment for horizontal and vertical outcomes.
  • Supports shared spacing, plus optional horizontal-only and vertical-only spacing.
  • Calls onLayoutDirectionChanged after the chosen axis changes.
  • Can lock the first chosen axis with maintainLayout to avoid flicker during resize animations.

When To Use It #

Use it for UI groups that should stay together as one row when space allows and become one column when space is tight:

  • Dialog or form actions.
  • Button groups and compact toolbars.
  • Filter chips or navigation controls that should not split across multiple runs.

How It Differs #

Wrap flows children into multiple rows or columns when they do not fit. SingleAxisWrap does not create runs. It chooses one complete row or one complete column.

OverflowBar is horizontal-first. SingleAxisWrap can be horizontal-first or vertical-first, with independent spacing, alignment, cross-axis alignment, direction-change callbacks, and optional layout locking.

Requirements #

  • Dart ^3.12.0
  • Flutter >=3.44.0
  • No runtime dependencies beyond Flutter

Installation #

Add the package to your Flutter project:

flutter pub add single_axis_wrap

Then import it:

import 'package:single_axis_wrap/single_axis_wrap.dart';

Usage #

A common use case is a button group that stays in a row when space allows and falls back to a column on narrow screens.

SingleAxisWrap(
  spacing: 8,
  horizontalAlignment: WrapAlignment.end,
  children: [
    TextButton(
      onPressed: () {},
      child: const Text('Cancel'),
    ),
    FilledButton(
      onPressed: () {},
      child: const Text('Save'),
    ),
  ],
)

By default, SingleAxisWrap tries Axis.horizontal first. If the row does not fit the available width, it uses Axis.vertical.

For vertical-first layout, set primaryDirection:

SingleAxisWrap(
  primaryDirection: Axis.vertical,
  spacing: 8,
  children: const [
    Chip(label: Text('Open')),
    Chip(label: Text('Assigned')),
    Chip(label: Text('Urgent')),
  ],
)

Direction Changes #

Use onLayoutDirectionChanged to react when the widget switches between row and column. Add maintainLayout: true when resizing or animation would otherwise make the layout flip repeatedly.

SingleAxisWrap(
  maintainLayout: true,
  spacing: 8,
  onLayoutDirectionChanged: (direction) {
    debugPrint('SingleAxisWrap changed to $direction');
  },
  children: [
    TextButton(onPressed: () {}, child: const Text('Cancel')),
    FilledButton(onPressed: () {}, child: const Text('Save')),
  ],
)

The callback is scheduled after layout and is not called for the initial layout. maintainLayout keeps the first chosen direction until it is turned off, primaryDirection changes, or the render object is recreated.

Configuration #

Property Description
children Widgets laid out together in one final row or one final column.
primaryDirection Axis attempted first. Defaults to Axis.horizontal.
spacing Default gap between adjacent children in either layout.
horizontalSpacing Gap used when the final layout is horizontal. Overrides spacing.
verticalSpacing Gap used when the final layout is vertical. Overrides spacing.
horizontalAlignment Main-axis alignment when the final layout is horizontal.
verticalAlignment Main-axis alignment when the final layout is vertical.
horizontalCrossAxisAlignment Cross-axis alignment when the final layout is horizontal.
verticalCrossAxisAlignment Cross-axis alignment when the final layout is vertical.
textDirection Resolves horizontal child order and horizontal start / end.
verticalDirection Resolves vertical child order and vertical start / end.
clipBehavior Controls paint clipping when children overflow.
onLayoutDirectionChanged Called after an existing resolved direction changes.
maintainLayout Reuses the first chosen direction until reset.
measurementStrategy Defaults to MeasurementStrategy.layout. Use intrinsic only after profiling.

Notes #

  • SingleAxisWrap always commits to exactly one row or one column.
  • It does not create multiple rows or multiple columns. Use Wrap for that.
  • If both axes are too small, the fallback layout can still overflow. Use clipBehavior to control paint clipping.
  • Alignment values like center and end only visibly move children when the parent gives SingleAxisWrap extra space.

Contributing #

Contributions are welcome. Please open an issue or pull request on GitHub.

License #

This project is licensed under the BSD 3-Clause License. See LICENSE for details.

Support #

Buy Me A Coffee

3
likes
160
points
125
downloads

Documentation

API reference

Publisher

verified publishertomars.tech

Weekly Downloads

A Flutter widget that chooses one complete row or column based on available space.

Repository (GitHub)
View/report issues

Topics

#layout #responsive #wrap #single #adaptive

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on single_axis_wrap