Flutter Unbounded height / width (無制限の高さエラー)

2021/07/26に公開

Unbounded height / width | Decoding Flutter

この YouTube の内容の文字起こしを行いました。

https://www.youtube.com/watch?v=jckqXR5CrPI

文字起こし

You put a ListView in a column like this, and you get an error, "Viewport was given unbounded height.

      body: Column(
        children: [
          SizedBox(height: 120),
          ListView(
            children: [
              Text(
                'You have pushed the button this many times:',
              ),
            ],
          ),
        ],
      ),

エラー文

════════ Exception caught by rendering library ═════════════════════════════════
The following assertion was thrown during performResize():
Vertical viewport was given unbounded height.

Viewports expand in the scrolling direction to fill their container. In this case, a vertical viewport was given an unlimited amount of vertical space in which to expand. This situation typically happens when a scrollable widget is nested inside another scrollable widget.

If this widget is always nested in a scrollable widget there is no need to use a viewport because there will always be enough vertical space for the children. In this case, consider using a Column instead. Otherwise, consider using the "shrinkWrap" property (or a ShrinkWrappingViewport) to size the height of the viewport to the sum of the heights of its children.

The relevant error-causing widget was
ListView

Well, the Flutter needs to perform layout quickly. In other words, it needs to decide where to put each widget on the screen very efficiently.

This makes your app faster and also more battery efficient.

For that reason, Flutter's layout algorithm is single pass.

  • Constraints go down.
  • Geometry goes up just one time.
  • There is no back and forth.

It looks kind of like this.

Padding) All right, child, this is how big or small you can get. (Constraint)

Child) OK, Dad. I want to be this size.

Padding) All right, you'll be here then.

This happens to all widgets. It starts from the very top, something like My App, and then it goes down to every leaf node, and then it goes back up again.

With things like column, things get a little more interesting. because column and widgets like that have several children.

Column) All right, it's time to lay out. If you could have any size you wanted, what would it be? (Constraints)

ChildA) I want 20 pixels, please.
ChildB) I want 30 pixels.

Column) All right, this is where you all will be.

That works pretty nicely, doesn't it? But watch what happens when one of the kids is a List View.

Column) All right, kids, it's that time again. If you could have any size you wanted, what would it be?

ChildA) I want 20 pixels, please.
ListView) I want everything.

Column) All right. Oh, wait, Wait what?
ListView) As much as possible.
Column) But that's infinity.

Column, ChildA) Oh... huh, huh

Column が何も制約を設けず、子供にどのサイズが欲しいと聞くと、
子のListView は高さを無制限に欲しいと、親 Column に無茶なことを言うので、そのタイミングでエラーが発生します。

And this is exactly what happens when you have an unbounded hight error in your app. Now, you might be thinking, why is column saying you can have any size you wanted? Why doesn't it limit the size of each of the children to its own size? Why doesn't it say, you can be as tell as I am.

Well, that would indeed remove the error, but it wouldn't remove the underlying problem that the error is trying to tell you about. Watch this.

Column) All right, kids, each of you can be at most as tall as I am, 100 pixels. What size do you want>

ListView) 100 pixels.
ChildB) Hey.

Column) All right, I guess ListView gets everything.

Column がもともと 100pixels の高さ制約を子供に伝えていれば、子供は無茶を言うことを抑えることができます。

As a developer, you see no error, but the final layout is probably not what you wanted. All the solutions that we can come up with to this problem mean that you either see the error, which is what's currently happening, or you get weird behavior but no error, so you don't know what's happening, or the column needs to talk to each child and do a little back and forth, which means the layout algorithm is much slower.

What does this all mean for you then?

So the good news is that if you have this problem, you know about it immediately. You get the unbounded height error from the Flutter framework.

Now, if you have this error, what should you be doing? You should be as explicit as possible.

For example, if you want he ListView to be as big as possible, still giving space to all the other children of the column, then just wrap it with Expanded or a Flex symbol. If, on the other hand, you want the ListView to be an exact science, for example, wrap it with SizedBox.

① Expanded, Flex で ListView を囲み、高さを Column に教える

      body: Column(
        children: [
          SizedBox(height: 120),
          Expanded(
            child: ListView(
              children: [
                Text(
                  'You have pushed the button this many times:',
                ),
              ],
            ),
          ),
        ],
      ),

② SizedBox で ListView を囲み、高さを Column に教える

      body: Column(
        children: [
          SizedBox(height: 120),
          SizedBox(
            height: 120,
            child: ListView(
              children: [
                Text(
                  'You have pushed the button this many times:',
                ),
              ],
            ),
          ),
        ],
      ),

しかし、この場合は、端末の文字サイズを大きくするとレイアウトエラーが発生するでお勧めしません。

All right, the next time you will see the dreaded unbounded height error, now you know why it exists and how to get out of it.

最後に

この動画はFlutterの以下のレイアウトの基本を知らないと理解できないと思います。

- Constraints go down.
- Geometry goes up just one time.
- There is no back and forth.

僕も Understanding constraints を見てレイアウトをもっと勉強する必要があると思いました。

それにしても、Flutterの公式YouTubeは有意義な内容ものばかりで、Community活性化に力を入れていて好きです。
今後もキャッチアップしていきたいですね。

Discussion