Understanding the Constraints System
Core of the Constraints System
Flitter's layout system follows one simple rule:
"Constraints go down, Sizes go up"
The parent passes constraints to the child, and the child determines its size within those constraints and reports back to the parent.
Constraints go down, Sizes go up
Parent Widget
|
+--------v--------+
| Constraints | (go down)
| min: 0x0 |
| max: 300x200 |
+--------+--------+
|
Child Widget
|
+--------v--------+
| Decides Size | (goes up)
| chosen: 150x80 |
+-----------------+
BoxConstraints Components
class BoxConstraints {
minWidth: number; // Minimum width
maxWidth: number; // Maximum width
minHeight: number; // Minimum height
maxHeight: number; // Maximum height
}The child can choose its size within these constraints:
minWidth <= width <= maxWidthminHeight <= height <= maxHeight
Key Constraints Patterns
1. Tight Constraints
Constraints.tight(new Size(100, 50))
// minWidth = maxWidth = 100
// minHeight = maxHeight = 50+---------------+
| Exactly 100x50 | <-- child is forced to this size
+---------------+
2. Loose Constraints
Constraints.loose(new Size(100, 50))
// minWidth = minHeight = 0
// maxWidth = 100, maxHeight = 50+---------------+
| 0x0 ~ 100x50 | <-- child freely chooses within range
+---------------+
3. Expand Constraints
Constraints.expand()
// minWidth = maxWidth = Infinity
// minHeight = maxHeight = Infinity+------------------+
| Maximum possible | <-- fills all parent space
+------------------+
4. Unconstrained
UnconstrainedBox({
child: Container({ width: 1000 }) // Ignores parent constraints
})Parent: max 300px
|
v
UnconstrainedBox --> Child: 1000px (overflow possible)
Understanding Real-World Behavior
Container vs SizedBox
// Container's width/height are "wishes"
Container({
width: 200, // If parent only allows 100px, becomes 100px
height: 100,
child: Text("Hello")
})
// SizedBox "forces" its size
SizedBox({
width: 200, // Forces exactly 200px to child
height: 100,
child: Text("Hello")
})Constraints Inside Column
Column (height: Infinity)
|
+-- Child 1 (height: tight natural size)
+-- Child 2 (height: tight natural size)
+-- Child 3 (height: tight natural size)
Column does not "tightly" constrain height for its children. Each child can take as much height as it wants.
Common Problems and Solutions
Problem 1: Infinite Size Error
// Wrong - Column inside Column receives infinite height
Column({
children: [
Column({ children: [...] }) // RenderFlex overflowed!
]
})
// Correct - Use Expanded to limit size
Column({
children: [
Expanded({
child: Column({ children: [...] })
})
]
})Problem 2: Widget Doesn't Get Desired Size
// Wrong - Parent passes tight constraints
Container({
width: 100,
height: 100,
child: Container({
width: 200, // Ignored! (forced to 100px)
height: 200
})
})
// Correct - Release constraints with UnconstrainedBox
Container({
width: 100,
height: 100,
child: UnconstrainedBox({
child: Container({
width: 200, // Applied (overflow may occur)
height: 200
})
})
})Problem 3: Center Alignment Doesn't Work
// Wrong - Container fits to parent size
Row({
children: [
Container({
color: 'red',
child: Text('Hello') // Container only takes text size
})
]
})
// Correct - Explicit size or use Expanded
Row({
children: [
Expanded({
child: Container({
color: 'red',
alignment: Alignment.center,
child: Text('Hello')
})
})
]
})Source Code Locations
packages/flitter/src/type/constraints.ts: BoxConstraints classpackages/flitter/src/renderobject/RenderBox.ts: Constraint handling logicpackages/flitter/src/component/Container.ts: Container's constraint handling
Key Takeaways
- Parent passes constraints -- child decides size -- reports to parent
- Constraints = size range (min/max width/height)
- tight: forces exact size, loose: only limits maximum size
- Understanding this makes layout problems easy to solve