resizable_columns 0.1.0
resizable_columns: ^0.1.0 copied to clipboard
A Flutter widget that lays out panes in a row or a column with draggable dividers. Supports nesting, minimum sizes, pushing neighbouring panes and right-to-left layouts.
ResizableColumns #
A Flutter widget that provides a flexible, resizable layout with draggable dividers. It allows you to create multi-pane layouts where users can resize the panes by dragging the dividers between them. Suitable for building responsive and interactive UIs that require adjustable panel sizes.
Features #
- Resizable panes: users adjust the size of child widgets by dragging the dividers.
- Horizontal and vertical orientations: panes sit side by side or on top of each other, and can be nested.
- Initial sizes and proportions: set the starting size of each pane in pixels or as a share of the space.
- Minimum pane size: a pane never shrinks below
minChildSizewhile the space allows it. - Customizable dividers: set the thickness and the color of the dividers.
- Push mode: a divider can go past a pane that is at its minimum and shrink the panes behind it.
- Easy to grab: a divider can be dragged by an area wider than the divider itself.
- Alignment: align each child within its pane.
- Responsive: panes keep their proportions when the parent is resized.
- Saving and restoring:
onSizesChangedreports the pane sizes while a divider is dragged. - Right-to-left aware: dividers follow the pointer in right-to-left layouts.
- Resize cursor: the mouse cursor changes over a divider on desktop and web.
Installation #
Add the following line to your pubspec.yaml under dependencies:
dependencies:
resizable_columns: ^0.1.0
Then, run:
flutter pub get
Import the package in your Dart code:
import 'package:resizable_columns/resizable_columns.dart';
Usage #
Basic example #
Two panes in a horizontal orientation, split equally:
ResizableColumns(
orientation: ResizableOrientation.horizontal,
dividerThickness: 8.0,
minChildSize: 100.0,
children: [
(context) => Container(color: Colors.red),
(context) => Container(color: Colors.blue),
],
);
Setting initial proportions #
initialProportions splits the space by share. Here the middle pane starts twice as wide as the others:
ResizableColumns(
orientation: ResizableOrientation.horizontal,
dividerThickness: 8.0,
initialProportions: const [1, 2, 1],
minChildSize: 100.0,
children: [
(context) => Container(color: Colors.red),
(context) => Container(color: Colors.blue),
(context) => Container(color: Colors.green),
],
);
Nesting #
A pane can hold another ResizableColumns with the other orientation:
ResizableColumns(
orientation: ResizableOrientation.horizontal,
children: [
(context) => Container(color: Colors.red),
(context) => ResizableColumns(
orientation: ResizableOrientation.vertical,
children: [
(context) => Container(color: Colors.blue),
(context) => Container(color: Colors.orange),
],
),
],
);
Pushing other panes #
By default a divider stops when the pane next to it reaches minChildSize. With ResizableDragMode.push it keeps
going and takes the space from the panes further along, one after another:
ResizableColumns(
orientation: ResizableOrientation.horizontal,
minChildSize: 100.0,
dragMode: ResizableDragMode.push,
children: [
(context) => Container(color: Colors.red),
(context) => Container(color: Colors.blue),
(context) => Container(color: Colors.green),
],
);
Dragging the first divider to the right shrinks the blue pane to 100 and then the green one. Dragging back during the same gesture gives them their space again.
Saving and restoring sizes #
onSizesChanged reports the size of every pane in pixels while a divider is dragged. Keep the last value and pass it
back as initialSizes to restore the layout:
ResizableColumns(
orientation: ResizableOrientation.horizontal,
initialSizes: savedSizes,
onSizesChanged: (sizes) => savedSizes = sizes,
children: [
(context) => Container(color: Colors.red),
(context) => Container(color: Colors.blue),
],
);
The callback is not called for the initial layout or when the parent is resized. Restored sizes are scaled to the space available, so the layout keeps its proportions in a window of a different size.
API reference #
ResizableColumns #
A widget that displays multiple resizable child widgets separated by draggable dividers.
const ResizableColumns({
super.key,
required this.children,
required this.orientation,
this.dividerThickness = 2.0,
this.dividerColor = const Color(0x00000000),
this.dividerHitSize = 12.0,
this.initialProportions,
this.initialSizes,
this.draggable = true,
this.dragMode = ResizableDragMode.adjacent,
this.alignment = Alignment.topLeft,
this.minChildSize = 50.0,
this.onSizesChanged,
})
| Parameter | Description |
|---|---|
children |
Functions that build the child widgets. Each one receives a BuildContext. |
orientation |
ResizableOrientation.horizontal or ResizableOrientation.vertical. |
dividerThickness |
The thickness of the dividers between panes. |
dividerColor |
The color of the dividers. Transparent by default. |
dividerHitSize |
The size of the area a divider can be dragged by. Never smaller than dividerThickness. |
initialProportions |
The initial share of the space for each pane. The length must match the number of children. |
initialSizes |
The initial size of each pane in pixels. The length must match the number of children. Cannot be combined with initialProportions. |
draggable |
Whether the dividers can be dragged. |
dragMode |
ResizableDragMode.adjacent stops at the next pane's minimum; ResizableDragMode.push goes on and shrinks the panes further along. |
alignment |
Alignment of each child within its pane. |
minChildSize |
The minimum size a pane can shrink to. |
onSizesChanged |
Called while a divider is dragged, with the size of every pane in pixels. |
Behavior #
- Sizes are proportions.
initialSizesthat do not add up to the available space are scaled to fill it, and panes keep their proportions when the parent is resized. - The minimum yields when it cannot fit. If the space is smaller than
minChildSizetimes the number of panes, the panes share it equally. - The drag area lies over the panes. It is
dividerHitSizewide, centered on the divider, and takes no space in the layout. A drag that starts there moves the divider, and a tap still reaches the pane under it. - A child gets the constraints of its pane. A child larger than its pane is shrunk to fit. To keep its size and
clip it instead, wrap it in
ClipRectandOverflowBox, or make it scrollable. - Panes are not rebuilt while dragging. The builders run when
ResizableColumnsitself is rebuilt or when something a pane depends on changes. - Initial values are read once. A later change of
initialSizesorinitialProportionsdoes not move the panes. - Changing the number of children resets the sizes to the initial ones.
draggable: falseremoves the drag areas. The dividers are still drawn, but they take no gestures and do not change the mouse cursor.- An unbounded parent needs
initialSizes. Inside a scroll view along the orientation there is no space to share, so the sizes are taken frominitialSizesas they are.
ResizableDragMode #
What a dragged divider does once the pane next to it cannot shrink.
enum ResizableDragMode {
adjacent,
push,
}
| Value | Description |
|---|---|
adjacent |
Only the two panes next to the divider change. The divider stops when one of them reaches minChildSize. This is the default. |
push |
The divider keeps going and takes the space from the panes further along, one after another. Only the pane it moves away from grows. |
ResizableOrientation #
The axis along which the panes are laid out and resized.
enum ResizableOrientation {
vertical,
horizontal,
}
| Value | Description |
|---|---|
vertical |
Panes are stacked from top to bottom and resized by height. |
horizontal |
Panes are placed side by side and resized by width. |
Example #
The example app shows four columns with different
content: a fixed-size box, a nested vertical layout with scrolling lists, a fixed-width list, and a child larger than
its pane. The outer row uses ResizableDragMode.push, the nested layout the default mode. Run it in Chrome:
cd example
flutter run -d chrome
Contributing #
Contributions are welcome! If you encounter any issues or have suggestions for improvements, please open an issue or submit a pull request on GitHub.
License #
This project is licensed under the MIT License - see the LICENSE file for details.