본문 바로가기
flutter/Widget of the Week

Flutter[플러터] / Stack 을 사용하여 위젯을 겹쳐서 배치하기 (겹치기, 포개기, 중첩, 스택, 포지션, Positioned) Stack(Flutter Widget of the Week)

by ch5c 2025. 6. 6.
반응형

Stack class

위젯의 자식 요소를 상자의 가장자리를 기준으로 배치하는 위젯입니다.

이 클래스는 여러 자식 요소를 간단한 방법으로 겹치게 하려는 경우에 유용합니다.

예를 들어 텍스트와 이미지를 겹쳐 놓고 그라디언트를 적용한 다음 아래쪽에 버튼을 붙인 경우입니다.

https://youtu.be/liEGSeD3Zt8

Stack 위젯의 각 자식 위젯은 위치가 지정되거나 지정되지 않습니다. 위치가 지정된 자식 위젯은 null이 아닌 속성을 하나 이상 가진 Positioned 위젯에 래핑된 위젯입니다. 스택은 모든 위치가 지정되지 않은 자식 위젯을 포함하도록 크기가 조정되며, 이 자식 위젯들은 정렬 (기본값은 왼쪽에서 오른쪽으로 읽는 환경에서는 왼쪽 위 모서리, 오른쪽에서 왼쪽으로 읽는 환경에서는 오른쪽 위 모서리)에 따라 배치됩니다. 위치가 지정된 자식 위젯들은 top, right, bottom, left 속성에 따라 스택을 기준으로 배치됩니다.

스택은 첫 번째 자식이 맨 아래에 오도록 자식들을 순서대로 채웁니다. 자식들이 채색되는 순서를 변경하려면, 새로운 순서로 자식들을 배치하여 스택을 다시 빌드할 수 있습니다. 이런 방식으로 자식들을 재정렬할 경우, 자식들에게 null이 아닌 키를 부여하는 것을 고려해 보세요. 이러한 키를 사용하면 프레임워크가 자식들의 기본 객체를 새 위치에 다시 생성하는 대신 새 위치로 이동합니다.

스택 레이아웃 알고리즘에 대한 자세한 내용은 RenderStack을 참조하세요.

여러 자식 요소를 특정 패턴으로 배치하거나 사용자 지정 레이아웃 관리자를 만들고 싶다면 CustomMultiChildLayout을 사용하는 것이 좋습니다. 특히 Stack을 사용할 때는 자식 요소의 크기나 스택 자체 크기를 기준으로 자식 요소의 위치를 ​​지정할 수 없습니다.

공식 문서 코드

 

 


기본적으로 위젯을 배치할 때 '쌓는다'는 것을 중점으로 그것이 가로가 되었든 세로가 되었는 순서에 맞게 위젯을 쌓게 되어있다.

그렇다면 가령 겹쳐진 모습을 만들고 싶을 때는 어떻게 해야 할까? 그럴 때에는 Stack 위젯을 사용해 주면 된다.

이 Stack 위젯은 자식 위젯들을 서로 "겹쳐서" 배치할 때 사용하는 레이아웃 위젯이다.

이 위젯의 주요 특징으로는 자식들을 순서대로 겹쳐서 표시하는 것에 있는데 가장 먼저 선언된 위젯이 맨 아래, 가장 마지막에 선언된 위젯이 맨 위에 올라오게 된다. 마치 레이어를 쌓는 것과 비슷하다고 보면 된다.

또한 자식 위젯들은 Positioned 위젯을 이용해서 Stack 안에서 정확한 위치를 지정할 수 있다. 한번 알아보자.

하위 속성
속성명 타입 기본값 설명
alignment AlignmentGeometry AlignmentDirectional.topStart 위치가 지정되지 않은 자식 위젯들의 정렬 기준
textDirection TextDirection? null 방향성을 해석할 때 사용할 텍스트 방향
fit StackFit StackFit.loose 내부의 자식 위젯에 적용할 크기 제약 방식
clipBehavior Clip Clip.hardEdge 경계를 넘어가는 자식 위젯을 잘라낼지 여부
children List<Widget> [] Stack에 쌓을 자식 위젯 목록

 

사용방법은 간단하다. Column 이나 Row 처럼 children 안에 위젯들을 배치해주면 된다.

단지 차이점으로는 Column 이나 Row 안에서는 위젯은 겹칠 수 없지만 Stack 안에서는 겹칠 수 있다는 것 정도이다.

Stack(
  children: [
    Container(width: 100, height: 100, color: Colors.blue,),
    Container(width: 75, height: 75, color: Colors.red,),
  ],
),

앞서 설명했듯이 이 코드에서는 먼저 선언된 위젯이 아래에 있게 된다. 흔히 포토샵 같은 거 사용할 때에는 레이어가 맨 위에 위치해야 화면에서도 젤 위에 위치되지만 Stack 에서는 그 반대이다. 말 그대로 '쌓는' 것이기 때문에 맨 위에 있는 것이 맨 아래가 되는 것이다.

기본적으로 Stack 의 정렬방식은 AlignmentDirectional.topStart, 왼쪽 상단 정렬이다. 이 정렬을 바꿔줄 수 있는데

Stack(
  alignment: Alignment.center,
  children: [
    Container(width: 100, height: 100, color: Colors.blue,),
    Container(width: 75, height: 75, color: Colors.red,),
  ],
),

이렇게 alignment: Alignment.center 를 먹여주면 중앙정렬을 갖게 된다.

사실 Stack 의 정수는 따로 있는데 바로 Positioned 이다. 이건 내가 전에 한번 가볍게 다뤘었다.

https://ch5c.tistory.com/44

 

Flutter[플러터] / Positioned 를 사용하여 Stack 안에서 자유롭게 위젯의 위치를 배치하기 (변경, 조절,

Positioned classStack 의 자식이 어디에 위치하는지 제어하는 ​​위젯입니다.Positioned 위젯 은 Stack 의 자손이어야 하며, Positioned 위젯에서 이를 둘러싼 Stack 까지 의 경로에는 StatelessWidget 또는 StatefulW

ch5c.tistory.com

Positioned 는 Stack 을 사용하는 이유라고 불러도 의문이 없을 정도로 Stack 그 자체인 위젯이니 꼭!꼭!꼭! 사용해야 한다.

Stack(
  children: [
    Container(width: 100, height: 100, color: Colors.blue,),
    Positioned( // 반드시 Stack 바로 아래에 위치 해야함
      left: 50,
      top: 50,
      child: Container(width: 75, height: 75, color: Colors.red,),
    ),
  ],
),

Positioned 는 자식 위젯을 Stack 아래에서 원하는 위치에 배치시키고 싶을 때 사용한다.

코드에서는 빨간 컨테이너를 왼쪽 상단으로부터 left: 50, top: 50 떨어진 위치에 배치 시켰다. 결과물은 아래와 같다.

오른쪽 빨간 컨테이너는 왼쪽 상단으로 부터 50, 50 씩 떨어져 있는 곳에 배치가 잘 되었다.

근데 여기서 특이한 점이 있는데 빨간 컨테이너의 크기가 75, 75인데 화면에 표시되는 것은 파란 상자의 크기인 100, 100까지 정도인 것 같다.

왜 그러냐 하면 Stack 은 다른 여타 위젯들과 마찬가지로 SizedBox 같은 위젯으로 크기를 직접 먹여주지 않는 한 자식의 크기만큼 크기를 갖게 되는데 이 코드에서는 가장 큰 자식은 파란 컨테이너이므로 파란 컨테이너의 크기를 갖게 된다.

그리고 그 100, 100의 크기에서 벗어나버린 위젯은 다 가차 없이 잘라버리는 것인데 이 문제의 해결방법으로는 크게 두 가지가 있다.

SizedBox(
  width: 125,
  height: 125,
  child: Stack(
    children: [
      Container(width: 100, height: 100, color: Colors.blue,),
      Positioned(
        left: 50,
        top: 50,
        child: Container(width: 75, height: 75, color: Colors.red,),
      ),
    ],
  ),
),

구성하는 앱의 레이아웃의 문제가 없다면 이게 가장 좋은 방법일 수 있다. 그냥 단순하게 Stack 의 크기를 늘려주면 된다.

이렇게 하면 빨간 컨테이너에 Positioned 가 잘 먹었다는 것을 알 수 있다.

그렇다면 두 번째 방법은 무엇인가? 바로 잘린 위젯을 어떻게 화면에 표시할지 결정하는 Stack 의 하위 속성을 건들면 된다.

바로 clipBehavior 인데 기본값으로는 Clip.hardEdge 을 갖고 있다. 이는 경계선을 넘어가는 위젯을 바로 잘라내어서 표시되지 않게 하겠다는 소리인데 이것을

Stack(
  clipBehavior: Clip.none, // Clip.none 사용
  children: [
    Container(width: 100, height: 100, color: Colors.blue,),
    Positioned(
      left: 50,
      top: 50,
      child: Container(width: 75, height: 75, color: Colors.red,),
    ),
  ],
),

Clip.none 을 사용하여 '넘어가는 위젯에 대하여 아무런 조치도 하지 않겠다'라고 선언해 주면 마찬가지로 화면에 잘 표시가 되게 된다.

사실 나는 이걸 좀 더 선호하고 많이 사용하는 편이다.

사실 Stack 을 이렇게 사용하기도 하지만 보통은 그냥 배경을 까는 용도로도 사용하곤 한다.

Stack(
  alignment: Alignment.center,
  children: [
    Image.asset(
      'assets/faker.jpeg',
      height: MediaQuery.sizeOf(context).height,
    ),
    Positioned(
      left: 50,
      top: 50,
      child: Container(width: 75, height: 75, color: Colors.red),
    ),
    Text(
      '숭배 해야만 해',
      style: TextStyle(
        fontSize: 24,
        color: Colors.white,
        fontWeight: FontWeight.bold,
      ),
    ),
  ],
),

이렇게 그냥 사진을 배경으로 깔아 두고 그 위에서 배치해 줄 수도 있다.

 

이렇게 간단히 Stack 위젯에 대해서 알아보았다.

이 위젯은 정말 UI 디자인에 있어서 필수불가결하게 사용될 수밖에 없는 그런 GOAT 위젯인데 이 Stack 을 얼마나 잘 쓰냐에 따라 화면이 얼마나 예뻐지느냐가 달려 있다고 해도 과언이 아닌 것 같다. (순수하게 기본 위젯들만 사용한다는 가정하에)

Positioned 를 사용할 때 오류 없이 잘 배치하여 사용하도록 하자. 도움이 되었길 바라며 마치겠다.

 

반응형