Layout System
Learn how to arrange widgets on screen using Shaft’s layout system.
Shaft provides powerful layout widgets like Column
, Row
, and Stack
to arrange your UI. The layout algorithm works by passing constraints down and sizes up - for details, see Layout Algorithm.
Layout Widgets
Column - Vertical Layout
Arranges children vertically from top to bottom.
Key properties:
mainAxisAlignment
: How to align children along vertical axis (.start
,.center
,.end
,.spaceBetween
,.spaceAround
,.spaceEvenly
)crossAxisAlignment
: How to align children horizontally (.start
,.center
,.end
,.stretch
)mainAxisSize
: Whether to take up all available vertical space (.max
) or just what children need (.min
)spacing
: Adds fixed spacing between children
Column(
mainAxisAlignment: .center,
crossAxisAlignment: .start,
mainAxisSize: .min,
spacing: 16
) {
Text("First")
Text("Second")
Text("Third")
}
Row - Horizontal Layout
Arranges children horizontally from left to right.
Key properties:
mainAxisAlignment
: How to align children along horizontal axiscrossAxisAlignment
: How to align children verticallymainAxisSize
: Whether to take up all available horizontal space (.max
) or just what children need (.min
)spacing
: Adds fixed spacing between children
Row(
mainAxisAlignment: .spaceBetween,
crossAxisAlignment: .center,
spacing: 8
) {
Text("Left")
Text("Center")
Text("Right")
}
Stack - Layered Layout
Places children on top of each other, like layers.
Key properties:
alignment
: How to align all children (.topLeft
,.center
,.bottomRight
, etc.)fit
: How to size non-positioned children (.loose
,.expand
)
Stack(alignment: Alignment.center) {
// Background
SizedBox(width: 200, height: 200)
.decoration(.box(color: Color(0xFF_BLUE)))
// Foreground
Text("Overlay")
.textStyle(.init(color: Color(0xFF_FFFFFF)))
}
Positioned children: Use Positioned
to place children at specific locations:
Stack {
// Background fills the stack
SizedBox.expand()
.decoration(.box(color: Color(0xFF_F0F0F0)))
// Top-left corner
Positioned(top: 10, left: 10) {
Text("Top Left")
}
// Bottom-right corner
Positioned(bottom: 10, right: 10) {
Text("Bottom Right")
}
}
Flexible Layouts
Expanded
Makes a child take up all remaining space in a Column or Row:
Row {
Text("Fixed")
Expanded(flex: 1) {
Text("Takes remaining space")
.decoration(.box(color: Color(0xFF_E0E0E0)))
}
Text("Fixed")
}
Multiple Expanded children share space based on their flex
value:
Row {
Expanded(flex: 1) { // Gets 1/3 of space
Text("1")
}
Expanded(flex: 2) { // Gets 2/3 of space
Text("2")
}
}
Flexible
Similar to Expanded, but allows the child to be smaller than the available space:
Row {
Flexible(flex: 1, fit: .loose) {
Text("Can be smaller than allocated space")
}
}
Difference:
Expanded
: Forces child to fill space (fit: .tight
)Flexible
: Allows child to be smaller (fit: .loose
)
Using Expanded for Flexible Space
Use Expanded
with an empty child to create flexible space:
Row {
Text("Left")
Expanded {
SizedBox() // Takes up remaining space
}
Text("Right")
}
For fixed spacing, use SizedBox
:
Column {
Text("Top")
SizedBox(height: 20) // Fixed 20pt spacing
Text("Bottom")
}
Or better, use the spacing
parameter:
Column(spacing: 20) {
Text("Top")
Text("Bottom")
}
Sizing Widgets
SizedBox
Forces a specific size:
SizedBox(width: 100, height: 50) {
Text("100x50 box")
}
// Expand to fill parent
SizedBox.expand() {
Text("Fills parent")
}
// Shrink to zero
SizedBox.shrink() // Useful for conditional rendering
Constraints
Add size constraints to any widget:
Text("Constrained")
.constrained(width: 200, height: 100)
// Or use minWidth/maxWidth
Text("Max width")
.constrained(maxWidth: 300)
Alignment Widgets
Center
Centers its child:
Center {
Text("I'm centered!")
}
// Or use the modifier
Text("Centered")
.center()
Align
Positions child with fine-grained control:
Align(alignment: Alignment.topRight) {
Text("Top Right")
}
// Or use the modifier
Text("Bottom Left")
.align(Alignment.bottomLeft)
Common alignments:
Alignment.topLeft
,Alignment.topCenter
,Alignment.topRight
Alignment.centerLeft
,Alignment.center
,Alignment.centerRight
Alignment.bottomLeft
,Alignment.bottomCenter
,Alignment.bottomRight
Padding & Spacing
Padding
Adds space around a widget:
Text("Padded")
.padding(.all(16)) // 16pt on all sides
Text("Custom padding")
.padding(.only(top: 8, bottom: 8, left: 16, right: 16))
Text("Symmetric")
.padding(.symmetric(vertical: 8, horizontal: 16))
Layout Debugging
Understanding Constraints
When layouts don’t work as expected, check:
What constraints is my widget receiving?
- Is parent giving tight or loose constraints?
- Are constraints bounded or unbounded?
What size is my widget choosing?
- Is it respecting parent constraints?
- Does it have intrinsic size requirements?
Where is my widget being placed?
- Check parent’s alignment settings
- Look for
Positioned
,Align
, orCenter
widgets
Next Steps
- Explore Widgets - All available layout widgets
- Learn about Widget Styling - Style your layouts
- Check out Tutorials - See layouts in action
Additional Resources
- Flutter’s Layout Algorithm - Detailed explanation (applies to Shaft)
- Shaft Source Code - See the implementation