Constraints 시스템의 핵심
Flitter의 레이아웃 시스템은 하나의 간단한 규칙을 따릅니다:
“Constraints go down, Sizes go up”
부모가 자식에게 제약(constraints)을 전달하고, 자식은 그 제약 내에서 자신의 크기를 결정하여 부모에게 보고합니다.
Constraints go down, Sizes go up
Parent Widget
│
┌────────▼────────┐
│ Constraints │ (go down)
│ min: 0x0 │
│ max: 300x200 │
└────────┬────────┘
│
Child Widget
│
┌────────▼────────┐
│ Decides Size │ (goes up)
│ chosen: 150x80│
└─────────────────┘
BoxConstraints의 구성 요소
class BoxConstraints {
minWidth: number; // 최소 너비
maxWidth: number; // 최대 너비
minHeight: number; // 최소 높이
maxHeight: number; // 최대 높이
}
자식은 이 제약 내에서 자신의 크기를 선택할 수 있습니다:
minWidth ≤ width ≤ maxWidth
minHeight ≤ height ≤ maxHeight
주요 Constraints 패턴
1. Tight Constraints
Constraints.tight(new Size(100, 50))
// minWidth = maxWidth = 100
// minHeight = maxHeight = 50
┌─────────────┐
│ 정확히 100x50 │ ← 자식은 이 크기로 강제됨
└─────────────┘
2. Loose Constraints
Constraints.loose(new Size(100, 50))
// minWidth = minHeight = 0
// maxWidth = 100, maxHeight = 50
┌─────────────┐
│ 0x0 ~ 100x50│ ← 자식이 범위 내에서 자유롭게 선택
└─────────────┘
3. Expand Constraints
Constraints.expand()
// minWidth = maxWidth = ∞
// minHeight = maxHeight = ∞
┌─────────────┐
│ 가능한 최대크기│ ← 부모 공간을 모두 차지
└─────────────┘
4. Unconstrained
UnconstrainedBox({
child: Container({ width: 1000 }) // 부모 제약 무시
})
Parent: max 300px
│
▼
UnconstrainedBox ──→ Child: 1000px (오버플로우 가능)
실제 동작 이해하기
Container vs SizedBox
// Container의 width/height는 "희망사항"
Container({
width: 200, // ← 부모가 100px만 허용하면 100px가 됨
height: 100,
child: Text("Hello")
})
// SizedBox는 자신의 크기를 "강제"
SizedBox({
width: 200, // ← 자식에게 정확히 200px 강제
height: 100,
child: Text("Hello")
})
Column 내부의 제약
Column (height: ∞)
│
├─ Child 1 (height: tight 자연스러운 크기)
├─ Child 2 (height: tight 자연스러운 크기)
└─ Child 3 (height: tight 자연스러운 크기)
Column은 자식들에게 높이를 “tight”하게 제한하지 않습니다. 각 자식이 원하는 만큼 높이를 가질 수 있습니다.
흔한 문제와 해결
문제 1: 무한 크기 에러
// ❌ Column 안의 Column은 무한 높이를 받음
Column({
children: [
Column({ children: [...] }) // RenderFlex overflowed!
]
})
// ✅ Expanded로 크기 제한
Column({
children: [
Expanded({
child: Column({ children: [...] })
})
]
})
문제 2: 위젯이 원하는 크기가 안 나옴
// ❌ 부모가 tight constraints 전달
Container({
width: 100,
height: 100,
child: Container({
width: 200, // 무시됨! (100px로 강제)
height: 200
})
})
// ✅ UnconstrainedBox로 제약 해제
Container({
width: 100,
height: 100,
child: UnconstrainedBox({
child: Container({
width: 200, // 적용됨 (오버플로우 발생할 수 있음)
height: 200
})
})
})
문제 3: 중앙 정렬이 안 됨
// ❌ Container가 부모 크기에 맞춰짐
Row({
children: [
Container({
color: 'red',
child: Text('Hello') // Container가 텍스트 크기만 가짐
})
]
})
// ✅ 명시적 크기 지정 또는 Expanded 사용
Row({
children: [
Expanded({
child: Container({
color: 'red',
alignment: Alignment.center,
child: Text('Hello')
})
})
]
})
소스 코드 위치
packages/flitter/src/type/constraints.ts
: BoxConstraints 클래스packages/flitter/src/renderobject/RenderBox.ts
: 제약 처리 로직packages/flitter/src/component/Container.ts
: Container의 제약 처리
핵심 정리
- 부모가 제약을 전달 → 자식이 크기 결정 → 부모에게 보고
- 제약 = 크기 범위 (min/max width/height)
- tight: 정확한 크기 강제, loose: 최대 크기만 제한
- 이해하면 레이아웃 문제 해결 쉬워짐
다음 장에서는 이러한 제약 시스템을 활용하여 실제로 RenderObject를 직접 구현해보겠습니다.