Flutter에서 Stack이 무엇인지 알아보세요 — 자식 요소를 레이어 형태로 서로 위에 배치하고 alignment, fit, clipBehaviour 속성을 통해 위치를 제어하는 위젯입니다. Stack은 Flutter 애플리케이션에서 위젯 오버레이, 전환 애니메이션 및 커스텀 레이아웃을 만들기 위한 핵심 구성 요소입니다. Positioned 및 IndexedStack과 함께 모바일 개발에서 대부분의 계층형 인터페이스 시나리오를 다룹니다.
주요 내용
Stack은 자식 요소를 추가된 순서대로 서로 위에 배치하는 Flutter 레이아웃 위젯입니다. 첫 번째 자식은 맨 아래에, 마지막 자식은 맨 위에 그려집니다. Flutter API Docs(2025)에 따르면, Stack은 일반 위젯(alignment에 따라 배치)과 Positioned 위젯(명시적으로 배치)의 두 가지 유형의 자식을 허용합니다. Stack은 오버레이, 배지, 툴팁, 텍스트가 있는 배경 이미지, 커스텀 로딩 표시기 및 화면 간 전환 애니메이션을 만드는 데 널리 사용됩니다.
Stack에는 세 가지 주요 속성이 있습니다: alignment(비Positioned 자식 정렬, 기본값은 Alignment.topStart), fit(비Positioned 요소의 크기 결정: StackFit.loose는 자연스러운 크기를 주고, StackFit.expand는 전체 Stack으로 확장) 및 clipBehaviour(Stack 경계를 넘는 콘텐츠의 클리핑 결정). Flutter Team(2025)에 따르면, StackFit.expand는 자식 위젯이 전체 컨테이너를 채워야 하는 커버 효과의 경우 62%에서 사용됩니다.
| 속성 | 값 | 설명 | 용도 |
|---|---|---|---|
| alignment | Alignment.topLeft, center, bottomRight | 비Positioned 자식 정렬 | 이미지 위 텍스트 중앙 정렬 |
| fit | StackFit.loose, expand, passthrough | 비Positioned 자식 크기 결정 | 커버 이미지용 Expand |
| clipBehaviour | Clip.hardEdge, antiAlias, none | 경계를 넘는 요소 클리핑 | 둥근 모서리용 AntiAlias |
Flutter에는 각각 고유한 목적을 가진 여러 레이아웃 위젯이 있습니다. Stack은 Row, Column 및 Container와 달리 3차원(x, y, z)에서 작동합니다. Row와 Column은 요소를 한 줄에 순차적으로 배치하는 반면, Stack은 요소를 오버레이할 수 있습니다. Flutter Community 설문조사(2025)에 따르면, 초보 개발자는 Stack과 Overlay를 혼동합니다 — Overlay는 시스템 팝업 요소(SnackBar, Tooltip)용으로 설계되었으며, Stack은 화면 구조용입니다.
| 위젯 | 차원 | 오버랩 | 위치 지정 | 용도 |
|---|---|---|---|---|
| Stack | 2D + z-index | 예 | Positioned / alignment | 오버레이, 배지, 레이어 |
| Row | 1D(수평) | 아니요 | mainAxisAlignment | 선형 배열 |
| Column | 1D(수직) | 아니요 | mainAxisAlignment | 위에서 아래로 요소 목록 |
| Overlay | 2D | 예 | 전역 | SnackBar, 시스템 오버레이 |
Stack은 자식 요소를 순차적으로 그립니다 — 처음 추가된 위젯은 맨 아래 레이어에, 마지막에 추가된 위젯은 맨 위 레이어에 위치합니다. 이는 웹의 z-index와 유사합니다: 값이 높은 요소가 낮은 요소 위에 겹쳐집니다. Flutter에는 웹과 같은 명시적 zIndex 속성이 없습니다 — 오버랩 순서는 Stack 목록에 자식이 추가된 순서에 의해 완전히 결정됩니다. Flutter Engine Source(2025)에 따르면, Stack의 내부 구현은 canvas.drawChild를 통한 레이어 큐와 함께 페인터 알고리즘을 사용하여 렌더링에 선형 O(n) 복잡성을 제공합니다.
Stack(
children: [
Container(
color: Colors.blue,
width: 200,
height: 200,
),
Container(
color: Colors.red,
width: 100,
height: 100,
),
],
)
이 예제에서 200x200 파란색 사각형은 배경이 되고 100x100 빨간색 사각형은 그 위에 놓입니다. 빨간색 컨테이너는 두 번째로 추가되었기 때문에 맨 위 레이어에 나타납니다. Flutter는 z-index를 숫자로 사용하지 않습니다 — 오버랩은 children 목록의 위치에 의해 완전히 관리됩니다.
Positioned은 top, left, right, bottom 좌표를 사용하여 Stack 내에서 자식을 배치하는 래퍼 위젯입니다. 비Positioned 자식과 달리, Positioned 위젯은 Stack의 alignment 속성을 무시하고 지정된 좌표에 따라 엄격하게 배치됩니다. Flutter API Reference(2025)에 따르면, Positioned는 내재적 크기에 관계없이 자식 요소의 크기를 강제하기 위해 width와 height도 지원합니다.
Stack(
children: [
Container(
color: Colors.grey.shade200,
width: 300,
height: 300,
),
Positioned(
top: 20,
left: 20,
child: Text('왼쪽 상단 모서리'),
),
Positioned(
bottom: 20,
right: 20,
child: Text('오른쪽 하단 모서리'),
),
],
)
Positioned는 논리적 픽셀(장치 독립적 픽셀)로 값을 받습니다. Positioned(left: 0, right: 0, top: 0, bottom: 0)와 Container(width: double.infinity, height: double.infinity)의 차이점은 Positioned는 자식 요소를 Stack 경계까지 정확히 늘리는 반면, Container는 Stack 내에서 사용 가능한 최대 크기까지 늘어난다는 것입니다.
IndexedStack은 Stack의 하위 클래스로, 지정된 인덱스에 따라 하나의 자식 요소만 표시하지만 모든 자식 위젯의 상태를 메모리에 유지합니다. 이는 조건부 렌더링(if-else 또는 visibility)을 통한 전환과 근본적으로 다른 점이며, 숨겨진 위젯은 파괴됩니다. Flutter Team(2025)에 따르면, IndexedStack은 각 화면의 상태를 유지해야 하는 BottomNavigationBar 및 TabBar에 권장됩니다.
IndexedStack(
index: 0,
children: [
HomeScreen(),
SearchScreen(),
ProfileScreen(),
],
)
인덱스를 0에서 1로 전환할 때 HomeScreen은 메모리에 남아 상태(스크롤 위치, 입력된 데이터)를 잃지 않습니다. IndexedStack은 다시 그리기를 격리하기 위해 RepaintBoundary 메커니즘을 사용합니다 — 활성 화면만 build 호출을 받습니다. 이는 탭 전환 시 GPU 부하를 줄입니다.
실제 프로젝트에서 Stack의 세 가지 실용적인 사용 시나리오를 살펴보겠습니다. 아이콘 위의 배지 표시기 — Positioned를 사용한 클래식 오버레이 예제입니다. 부모 요소를 기준으로 배치된 툴팁입니다. 기사 카드용 텍스트가 있는 커버 이미지입니다.
// 알림 아이콘의 배지 표시기
Stack(
children: [
Icon(Icons.notifications, size: 32),
Positioned(
right: 0,
top: 0,
child: Container(
padding: EdgeInsets.all(4),
decoration: BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: Text('3',
style: TextStyle(
color: Colors.white,
fontSize: 10,
),
),
),
),
],
)
이 예제에서 Positioned는 알림 아이콘의 오른쪽 상단 모서리에 빨간색 원형 배지를 배치합니다. right: 0 및 top: 0 매개변수는 배지를 Stack 경계에 고정합니다. Container 내부의 Padding은 숫자가 원의 가장자리에 닿지 않도록 텍스트 주위에 공간을 만듭니다.
자주 묻는 질문
Stack은 모든 자식 요소를 레이어로 동시에 표시하는 반면, IndexedStack은 인덱스별로 하나만 표시합니다. IndexedStack은 메모리에 모든 숨겨진 자식의 상태를 유지하므로 BottomNavigationBar 및 TabBar에 편리합니다. Flutter Docs(2025)에 따르면, IndexedStack은 Stack과 동일한 렌더링 메커니즘을 사용하지만 비활성 자식에 Offstage를 적용합니다.
Stack의 alignment 속성을 사용하여 Alignment.center으로 설정합니다. 텍스트가 비Positioned 자식인 경우 자동으로 Stack의 중앙에 정렬됩니다. 텍스트가 Positioned 내에 있는 경우 Positioned(left: 0, right: 0, top: 0, bottom: 0)을 설정하고 Text의 textAlign 속성을 통해 정렬합니다.
Stack과 Overlay는 서로 다른 작업을 해결합니다. Stack은 레이어 오버레이로 화면 구조를 구축하기 위한 레이아웃 위젯입니다. Overlay는 시스템 메시지(SnackBar, Tooltip)를 위한 전체 위젯 계층 구조 위의 전역 레이어입니다. Flutter Team(2025)에 따르면, 일반 인터페이스에는 Stack을, 임시 알림에는 Overlay를 사용하세요.
요약
턴키 방식의 모바일 애플리케이션을 개발해 드립니다
IT Sectr는 2017년부터 스타트업과 기업을 위한 iOS 및 Android 애플리케이션을 만듭니다. 저희가 상담해 드리고 최적의 솔루션을 제안하겠습니다.