개요
Positioned는 Stack 위젯 내에서 자식 위젯의 정확한 위치를 지정하는 위젯입니다.
Positioned를 사용하면 Stack의 가장자리로부터의 거리를 지정하여 자식 위젯을 절대적으로 배치할 수 있습니다. CSS의 position: absolute
와 유사한 개념으로, top, bottom, left, right 속성을 통해 위치를 제어합니다. Positioned는 반드시 Stack의 직접적인 자식이어야 합니다.
언제 사용하나요?
- Stack 내에서 위젯을 특정 위치에 고정할 때
- 오버레이나 플로팅 요소를 만들 때
- 배지나 알림 표시를 다른 위젯 위에 겹칠 때
- 커스텀 레이아웃을 구성할 때
- FAB(Floating Action Button)과 같은 UI 요소를 배치할 때
기본 사용법
Stack({
children: [
// 배경 위젯
Container({ color: 'lightgray' }),
// 왼쪽 상단에 위치
Positioned({
top: 10,
left: 10,
child: Text('왼쪽 상단')
}),
// 오른쪽 하단에 위치
Positioned({
bottom: 10,
right: 10,
child: Text('오른쪽 하단')
})
]
})
Props
위치 지정 Props
top
값: number | undefined
Stack의 상단 가장자리로부터의 거리를 픽셀 단위로 지정합니다.
Positioned({
top: 20,
child: Text('상단에서 20픽셀')
})
bottom
값: number | undefined
Stack의 하단 가장자리로부터의 거리를 픽셀 단위로 지정합니다.
Positioned({
bottom: 20,
child: Text('하단에서 20픽셀')
})
left
값: number | undefined
Stack의 왼쪽 가장자리로부터의 거리를 픽셀 단위로 지정합니다.
Positioned({
left: 20,
child: Text('왼쪽에서 20픽셀')
})
right
값: number | undefined
Stack의 오른쪽 가장자리로부터의 거리를 픽셀 단위로 지정합니다.
Positioned({
right: 20,
child: Text('오른쪽에서 20픽셀')
})
크기 지정 Props
width
값: number | undefined
자식 위젯의 너비를 명시적으로 지정합니다. left와 right를 모두 지정한 경우 width는 무시됩니다.
Positioned({
top: 10,
left: 10,
width: 100,
height: 50,
child: Container({ color: 'blue' })
})
height
값: number | undefined
자식 위젯의 높이를 명시적으로 지정합니다. top과 bottom을 모두 지정한 경우 height는 무시됩니다.
Positioned({
top: 10,
left: 10,
width: 100,
height: 50,
child: Container({ color: 'red' })
})
child (필수)
값: Widget
위치를 지정할 자식 위젯입니다.
Positioned({
top: 50,
left: 50,
child: Icon({ icon: Icons.star, color: 'yellow' })
})
정적 메서드
Positioned.fill()
Stack 전체를 채우는 Positioned 위젯을 생성합니다. 모든 가장자리에서 0의 거리를 가집니다.
Stack({
children: [
Image({ src: 'background.jpg' }),
Positioned.fill({
child: Container({
color: 'rgba(0, 0, 0, 0.5)' // 반투명 오버레이
})
})
]
})
// 다음과 동일합니다:
Positioned({
top: 0,
bottom: 0,
left: 0,
right: 0,
child: Container({ color: 'rgba(0, 0, 0, 0.5)' })
})
위치와 크기 조합
대변 제약을 통한 크기 지정
left와 right를 모두 지정하면 너비가 자동으로 계산되고, top과 bottom을 모두 지정하면 높이가 자동으로 계산됩니다.
// 너비가 자동 계산됨 (Stack 너비 - 40)
Positioned({
top: 10,
left: 20,
right: 20,
child: Container({ height: 50, color: 'green' })
})
// 높이가 자동 계산됨 (Stack 높이 - 40)
Positioned({
top: 20,
bottom: 20,
left: 10,
child: Container({ width: 50, color: 'blue' })
})
실제 사용 예제
예제 1: 배지 표시
const notificationIcon = Container({
width: 50,
height: 50,
child: Stack({
children: [
Icon({ icon: Icons.notifications, size: 40 }),
Positioned({
top: 0,
right: 0,
child: Container({
width: 20,
height: 20,
decoration: BoxDecoration({
shape: 'circle',
color: 'red'
}),
alignment: Alignment.center,
child: Text('3', {
style: TextStyle({ color: 'white', fontSize: 12 })
})
})
})
]
})
});
예제 2: 이미지 위 텍스트 오버레이
const imageWithOverlay = Container({
width: 300,
height: 200,
child: Stack({
fit: StackFit.expand,
children: [
Image({ src: 'landscape.jpg', fit: 'cover' }),
// 하단 그라데이션
Positioned({
bottom: 0,
left: 0,
right: 0,
height: 80,
child: Container({
decoration: BoxDecoration({
gradient: LinearGradient({
begin: Alignment.bottomCenter,
end: Alignment.topCenter,
colors: ['rgba(0,0,0,0.8)', 'transparent']
})
})
})
}),
// 텍스트
Positioned({
bottom: 20,
left: 20,
right: 20,
child: Column({
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Beautiful Landscape', {
style: TextStyle({
color: 'white',
fontSize: 24,
fontWeight: 'bold'
})
}),
Text('Photo by John Doe', {
style: TextStyle({
color: 'rgba(255,255,255,0.8)',
fontSize: 14
})
})
]
})
})
]
})
});
예제 3: 플로팅 액션 버튼
const screenWithFAB = Scaffold({
body: Stack({
children: [
// 메인 컨텐츠
ListView({
children: List.generate(20, (i) =>
ListTile({ title: Text(`항목 ${i + 1}`) })
)
}),
// FAB
Positioned({
bottom: 16,
right: 16,
child: FloatingActionButton({
onPressed: () => console.log('FAB 클릭'),
child: Icon({ icon: Icons.add })
})
})
]
})
});
예제 4: 툴팁 표시
const tooltipExample = Stack({
clipped: false, // 툴팁이 Stack 영역을 벗어날 수 있게 함
children: [
ElevatedButton({
onPressed: () => {},
child: Text('Hover me')
}),
if (showTooltip) Positioned({
bottom: 40,
left: -20,
child: Container({
padding: EdgeInsets.all(8),
decoration: BoxDecoration({
color: 'black',
borderRadius: BorderRadius.circular(4)
}),
child: Text('This is a tooltip', {
style: TextStyle({ color: 'white' })
})
})
})
]
});
주의사항
- Positioned는 반드시 Stack의 직접적인 자식이어야 합니다.
- top과 bottom을 모두 지정하면 height는 무시됩니다.
- left와 right를 모두 지정하면 width는 무시됩니다.
- 최소한 하나의 위치 속성(top, bottom, left, right)을 지정해야 합니다.
- Stack의 크기가 결정되지 않은 경우 Positioned 위젯의 위치가 예상과 다를 수 있습니다.
- Positioned 위젯은 Stack의 크기 계산에 영향을 주지 않습니다.
관련 위젯
- Stack: Positioned가 사용되는 부모 위젯
- Align: 단일 자식을 정렬하는 더 간단한 방법
- Container: alignment 속성으로 간단한 정렬 가능
- Transform: 위치 대신 변형을 통한 배치
- CustomMultiChildLayout: 더 복잡한 레이아웃이 필요할 때