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.topRightAlignment.centerLeft,Alignment.center,Alignment.centerRightAlignment.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, orCenterwidgets
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