ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Flutter에서 Container(width)가 안 먹는 이유
    AI Agent/flutter 2026. 7. 2. 09:00
    728x90
    반응형

    중첩된 사각형 스케치가 그려진 노트와 자가 놓인 책상
    width: 300이 무시되던 삼십 분의 기록

    width: 200240으로 바꿔도 화면은 그대로였습니다.
    300으로 해봐도 똑같았습니다.
    숫자만 바뀌고 파란 박스의 폭은 1픽셀도 안 움직였습니다.
    삼십 분쯤 이걸 붙잡고 있었습니다.
    hot reload가 안 먹나 싶어서 앱을 껐다 켜기도 했고요.

    Flutter 레이아웃은 자식이 "나 200이야"라고 선언하면 그대로 되는 구조가 아니었습니다.
    부모가 먼저 "너는 이 안에서만 커라"라고 제약을 내려보내고, 자식은 그 제약 안에서 크기를 고릅니다.
    제 파란 박스는 부모가 이미 폭을 정해둔 자리에 앉아 있었습니다.

    제약이 내려가고, 크기가 올라옵니다

    그 시간 동안 제가 한 짓을 돌아보면 좀 웃깁니다.
    200을 240으로, 240을 300으로, 안 되니까 이번엔 반대로 150으로.
    숫자만 위아래로 흔들었습니다.
    중간엔 color를 빨강으로 바꿔서 박스가 화면에 있긴 한지부터 확인했고요.
    박스는 분명히 있었습니다.
    폭만 제 말을 안 들었을 뿐이죠.

    문서에서 본 한 문장이 그제서야 걸렸습니다.
    "Constraints go down.
    Sizes go up.
    Parent sets position." 제약은 부모에서 자식으로 내려가고, 크기는 자식에서 부모로 올라가고, 위치는 부모가 정한다.
    외울 땐 무슨 말인지 몰랐는데, 안 움직이는 박스 앞에서 보니 뜻이 살아났습니다.

    제 코드는 이랬습니다.

    Row(
      children: [
        Expanded(
          child: Container(width: 300, height: 60, color: Colors.blue),
        ),
        Text('옆 칸'),
      ],
    )
    

    문제는 Expanded였습니다.
    Expanded는 자식에게 "가로는 남는 만큼 딱 이만큼 채워"라는 제약을 내려보냅니다.
    이 제약은 최솟값과 최댓값이 같은, 이른바 tight 제약입니다.
    폭이 하나로 못 박혀 있으니 자식은 크기를 고를 여지가 없습니다.
    그러니 Container에 적은 width: 300은 읽히지도 않고 버려집니다.
    숫자를 아무리 바꿔도 화면이 그대로였던 건 이 때문이었습니다.
    제가 바꾸던 건 자식의 희망 사항이었고, 실제 결정권은 부모가 내려보내는 제약에 있었습니다.

    Layout Explorer가 보여준 숫자

    DevTools의 Layout Explorer를 연 게 그때가 처음이었습니다.
    위젯을 클릭하니 부모가 자식에게 내려보낸 제약이 그대로 숫자로 떠 있었습니다.
    BoxConstraints(w=..., h=...) 형태로요.
    제가 준 200이나 300이라는 값이 부모의 제약 범위와 어떻게 부딪히는지가 한눈에 보였습니다.
    콘솔에 print 찍던 것과는 다른 종류의 정보였습니다.
    화면이 왜 이 크기인지를, 위젯 트리의 위아래 방향으로 읽게 해줬거든요.

    클릭한 순간 제일 먼저 눈에 들어온 건 width의 최솟값과 최댓값이 똑같은 숫자였다는 점입니다.
    w=132.0처럼 값 하나로 딱 고정돼 있었죠.
    최솟값과 최댓값이 같다는 건 "이 폭이어야 한다"는 명령이지 "이 안에서 골라라"가 아닙니다.
    제가 준 300은 그 사이에 낄 자리가 없었습니다.
    그 132라는 숫자도 제가 어디에도 적은 적 없는 값이었습니다.
    옆 칸 텍스트를 놓고 Row에 남은 가로를 Expanded가 통째로 자식한테 넘긴 결과였습니다.

    콘솔에 print로 width를 찍었으면 132라는 결과만 봤지, 그게 왜 132인지는 몰랐을 겁니다.
    Layout Explorer는 그 숫자가 위에서 어떻게 정해져 내려왔는지를 트리로 보여줬습니다.
    print가 결과라면 이건 경로였습니다.
    부모 노드를 하나씩 접었다 펴면서, 어느 위젯이 tight 제약을 걸었는지를 손가락으로 짚어갈 수 있었습니다.

    (사실 이 도구가 있는 걸 그날 처음 안 게 조금 부끄러웠습니다.
    반나절도 아니고 그 짧은 시간을 숫자만 바꾸며 날렸으니까요.)

    Expanded를 벗기니 폭이 돌아왔습니다

    돌아보니 제가 헷갈린 건 의도 두 개가 섞여 있어서였습니다.
    남는 공간을 다 채우고 싶으면 Expanded가 맞고, 딱 300 폭이 필요하면 Expanded로 감싸면 안 됩니다.
    저는 300을 원하면서 Expanded로 싸매고 있었으니, 서로 반대되는 말을 코드에 동시에 적어둔 셈이었습니다.
    그래서 감싼 걸 벗겼습니다.

    Row(
      children: [
        Container(width: 300, height: 60, color: Colors.blue),
        Text('옆 칸'),
      ],
    )
    

    RowExpanded로 감싸지 않은 자식에게는 loose 제약을 줍니다.
    "0부터 남는 만큼 사이에서 네가 골라"라는 뜻이라, 이제 Container의 width: 300이 그 범위 안에서 존중됩니다.
    최솟값이 0이고 최댓값이 넉넉하니 300이 그 사이에 편하게 들어앉은 거죠.
    hot reload를 누르자마자 파란 박스가 300 폭으로 딱 서고 옆 칸이 그 오른쪽에 붙었습니다.
    한참을 안 움직이던 게 한 번에 자리를 잡으니 좀 허무하더라고요.

    이제 박스가 안 움직이면

    요즘은 위젯 크기가 코드대로 안 나오면 숫자를 바꾸는 대신 Layout Explorer부터 엽니다.
    그 위젯에 실제로 어떤 제약이 내려오고 있는지를 봅니다.
    대개는 제가 준 값이 무시된 게 아니라, 부모가 이미 다른 말을 하고 있었던 거였습니다.

    아직도 IntrinsicWidth나 높이가 정해지지 않은 자리는 감이 덜 옵니다.
    스크롤 안에 또 스크롤을 넣을 때 나는 그 빨간 에러 화면은 매번 문서를 다시 봅니다.
    그건 다음 삽질의 몫으로 남겨뒀습니다.

    728x90
    반응형
Designed by Tistory.