blog

[Flutter] Flutter Inspectorでレイアウトの問題をデバッグするには?

Flutter開発者として、アプリで画像が切れてしまう問題に遭遇したことがあるでしょう。もしかしたら "viewport was given unbounded height "というエラーが出たかも...

Feb 6, 2020 · 11 min. read
シェア

サブタイトル:発生の理由と解決策を探る

公開: 27 July 2020 - 9 min read

GIFFlutter InspectorのDetails TreeとLayout Explorer機能を表示しています。

Flutter開発者として、アプリでイメージが切れてしまう問題に遭遇したことがあるかもしれません。もしかしたら "viewport was given unbounded height "というエラーが出たかもしれません。 実際、最も一般的なFlutterのエラーの2つはレイアウトの問題です:ウィジェットのオーバーフローと "renderbox not laid out "の問題です。レイアウトの問題を経験しているのはあなただけではありません。

幸いなことに、Dart DevToolのFlutter Inspector使えば、なぜそのような問題が起こるのかを理解し、修正するのに役立ちます。この記事では、3つのよくあるレイアウトの問題をデバッグすることで、ツールの使い方を学びます。次に問題が発生したときには、プロのように修正できるようになります!

目次

  1. Flutter Inspectorとは?
  2. デバッグの冒険
    • オーバーフロー
    • 高さ無限エラー
    • 見えない垂直ディバイダー
  3. まとめ

Flutter Inspectorとは?

Flutter Inspectorはウィジェットツリーを探索し、視覚化するためのツールです。アプリのレイアウトの内部と外部を発見するのに最適なツールです。下のGIFでツールを見てみましょう。

Flutter InspectorのDetails TreeとLayout Explorer機能を示すGIF。

インスペクタを使えば、アプリケーション上のウィジェットを選択したり、デバッグバナーを削除することもできます。なぜウィジェットが見えないのか、Rowの子にフレックスを追加すると UI にどのような影響があるのか、などなど。この記事では、以下の機能に焦点を当てます:

  • 詳細ツリー - 各ウィジェットのプロパティを調べることができます。ウィジェットの実際のサイズを確認し、制約が親ウィジェットからどのように受け継がれるかを見ることができます。
  • Layout Explorer - Flexウィジェットとその子ノードを視覚化できます。Flex、フィット、軸の配置を調整することで、実行中のアプリケーションの変更を確認できます。

この時点で、"この素晴らしいツールを試すにはどうしたらいいのだろう?"と思っていることでしょう。 アプリケーション上で Dart DevTools を実行するだけで、インスペクタが最初に表示されるツールになります。詳細については、Dart DevTools参照してください。

デバッギングアドベンチャー| Developers.IO

まずは3つのレイアウトの問題を含むデモアプリをデバッグしてみましょう。Flutter Inspectorを使ってそれぞれの問題を個別にデバッグし、最後に修正を組み合わせて次のスクリーンショットのようなシンプルなメニューアプリを完成させます。

メニューを修正したアプリケーションの終了

レイアウトの問題に対処する際には、以下の手順を使用してください。覚えやすいように、COINの頭字語を使いましょう。この言葉を発明したのは?さっきの私です。

  1. デバッグコンソールのエラーメッセージを調べて、エラーのタイプとその原因となったウィジェットを特定してください。
  2. レイアウトエクスプローラーを開いて、Flexウィジェットとその子ウィジェットを表示します。
  3. 詳細ツリーで、エラーの原因となっているウィジェットのサイズと制限、およびその親/子を確認してください。
  4. コードに戻って問題を修正してください。

この記事の続きは、あなたのパソコンで読んでください。お気に入りのテキストエディタやIDEを開いて、一緒に冒険に出かけましょう!

  1. menuという新しいFlutterプロジェクトを作成します。
$ flutter create menu
  1. lib/main.dartファイルの内容を置き換えます。各レイアウト問題は、アプリケーション本体のExample1から始まるコード内で、独自のExampleクラスに分離されています。lib/main.dartを以下のコードに置き換えます:
import 'package:flutter/material.dart';
void main() {
 runApp(Menu());
}
class MenuItem extends StatelessWidget {
 const MenuItem(this.icon, this.itemText);
 final String icon;
 final String itemText;
 @override
 Widget build(BuildContext context) {
 return ListTile(
 leading: Text(
 icon,
 style: TextStyle(
 fontSize: 30.0,
 ),
 ),
 title: Text(itemText),
 );
 }
}
class Menu extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return MaterialApp(
 title: 'Flutter Demo',
 home: Scaffold(
 appBar: AppBar(
 title: Text('Menu Demo'),
 ),
 body: Padding(
 padding: EdgeInsets.all(20.0),
 child: Column(
 children: [
 // Modify code here
 Example1(),
 ],
 ),
 ),
 ),
 );
 }
}
// Problem 1: Overflow error
class Example1 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return Padding(
 padding: EdgeInsets.only(bottom: 30.0),
 child: Row(
 children: [
 Text(
 'Explore the restaurant\'s delicious menu items below!',
 style: TextStyle(
 fontSize: 18.0,
 ),
 ),
 ],
 ),
 );
 }
}
// Problem 2: Viewport was given unbounded height error
class Example2 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return ListView(
 children: [
 MenuItem('', 'Burger'),
 MenuItem('', 'Hot Dog'),
 MenuItem('', 'Fries'),
 MenuItem('', 'Soda'),
 MenuItem('', 'Ice Cream'),
 ],
 );
 }
}
// Problem 3: Invisible VerticalDivider
class Example3 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return Row(
 mainAxisAlignment: MainAxisAlignment.spaceEvenly,
 children: [
 RaisedButton(
 onPressed: () {
 print('Pickup button pressed.');
 },
 child: Text(
 'Pickup',
 ),
 ),
 // This widget is not shown on screen initially.
 VerticalDivider(
 width: 20.0,
 thickness: 5.0,
 ),
 RaisedButton(
 onPressed: () {
 print('Delivery button pressed.');
 },
 child: Text(
 'Delivery',
 ),
 )
 ],
 );
 }
}
  1. アプリケーションを実行します。
  2. を開いてください Dart DevTools

レイアウトの問題1:オーバーフローエラー

アプリを起動すると、ラインの端に黄色と黒の斜めのバーDart DevTools表示されます。

これはオーバーフローエラーが発生していることを意味します。では、デバッグのステップに従って問題を突き止め、正しい修正方法を見つけましょう。

  1. コンソールのエラーメッセージを確認してください。

コンソールのオーバーフローエラーのデバッグ

エラーメッセージによると、main.dartの54行目にあるRowに問題があるようです。RowはFlexウィジェットなので、Layout Explorerで確認することができます。

  1. レイアウトエクスプローラーを開きます。

DevToolsに移動し、Layout Explorerタブを開きます。

レイアウトエクスプローラーのオーバーフローエラー

」をクリックします。(イメージ上の数字は次のステップに関連しています)。

  1. 問題を示す赤いバナーが下部に表示されます。これらのバナーをよく見ると、Textが親ウィジェットのRowよりも幅が広く、オーバーフローエラーを引き起こしていることがわかります。

  2. Textの幅はRowと同じだけで、それ以上にはできません。Textのflexを1にしてみると、Textが縮小され、赤いバナーが消えます。ふぅ、直ったようです。まだまだです! このツールはあなたのコードには触れません。このツールは、レイアウトのプロパティを変更するとどうなるかを示しているだけなのです。

さておき。行と列のすべての子要素がデフォルトで展開されないことを不思議に思うかもしれません。

Flutterチームはこのような設計上の決定を下しました。もしすべての子がデフォルトでExpandedに設定されていたら、一部の子が拡大されすぎたり引き伸ばされたりするなど、他のレイアウトの問題が発生するでしょう。

  1. 詳細ツリーでサイズと制約をチェックします。この場合、問題はすでに特定されているので、このステップは省略できます。

  2. コードに戻って修正してください。

スマート・リファクタリングを使って、VS Code上でテキストを折り返します。

デフォルトのフレックスは1なので、このプロパティを指定する必要はありません。

レイアウトの問題2:高さ無制限エラー

次の例に移り、Column内のExample1()をExample2()に置き換えてホットリロードします。

Column(
 children: [
 // Modify code here
 Example2(),
 ],
)

Example2クラスには様々なメニュー項目を持つListViewがありますが、アプリケーションには何も表示されません:

一体何が起こったんですか?

  1. コンソールでエラーメッセージを確認してください。

試運転コンソールの高さ制限なしエラー

main.dartの72行目のListViewで "Vertical viewport was given unbounded height "というエラーが発生します。一見すると、Vertical viewportとunboundedという用語がはっきりしないので、次のステップに進みます。

  1. レイアウトエクスプローラーを開きます。

DevToolsに戻り、Layout Explorerタブを開きます。

レイアウトエクスプローラーに孫のFlexウィジェットが表示されません。

上部の更新アイコンをクリックしてツリーを更新します。レイアウトエクスプローラはFlexウィジェットとその直接の子ウィジェットしかサポートしていないため、ListViewをクリックしても何も表示されません。興味深いことに、Example2とColumnをクリックしても何も表示されず、レイアウトエクスプローラーは空白のままです。次のステップに進みます。

  1. 詳細ツリーでサイズと制約をチェックします。

詳細ツリーのListViewの制約とサイズ。

描画ウィジェットの情報を含むListViewの最初のrenderObjectを展開します。

  1. オレンジ色のテキストは、サイズが欠けていることを示しています。
  2. 制約プロパティを見た後、高さ制約が無限としてリストされていることに気づきました。ListViewは「ビューポート」であり、スクロール方向に高さが与えられていますCordon。

制約は、親ウィジェットから受け継がれます。ウィジェットがどのように制約を決定するかのスナップショットを示します。

列:好きなだけ高さを取ってください ListView:わかりました、私が全部取ります。Column:うわぁ、でもそれって、ちょっと。

また、ウィジェットは何をすべきか分かりません.ListViewが無限の高さを望んでいるため、サイズが決定できません。

さておき。カラムはなぜ子供の高さを自分の高さに制限しないのですか?

最初の子プロセスがすべてのスペースを占有してしまい、2番目の子プロセスの高さが0にならざるを得ないという状況が発生するかもしれません。

  1. コードに戻って修正してください。
class Example2 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return Expanded(
 child: ListView(
 ...
 ),
 );
 }
}

Expandedでウィジェットをラップすると、親の主軸に沿った_無制限の制約が与えられることを思い出してください。この場合、親はColumnなので、Expandedは高さの制約を提供します。ListViewをExpandedでラップし、ホットリロードすると、アプリ上にリストが表示されます。

レイアウトの問題3:見えない縦の仕切り

では、ColumnのExample2()をExample3()に置き換えてください。

Column(
 children: [
 // Modify code here
 Example3(),
 ],
)

サンプル Example3 クラスのコードを詳しく見てみましょう。VerticalDividerは存在しますが、ホットリロード後のアプリケーションには2つのボタンしか表示されないことに注目してください:

なぜ -VerticalDivider- は見えないのですか?

  1. コンソールでエラーメッセージを確認します。今回はエラーメッセージは表示されません。次のステップに進んでください。

  2. Layout Explorer を開きます。DevTools に戻り、[Layout Explorer] タブをクリックします。

エクスプローラーでの垂直スプリッターのレイアウト

ツリーを更新した後、VerticalDividerをクリックし、レイアウトエクスプローラの右側にスクロールします。VerticalDividerの幅と高さに制限がないことを確認してください。

1.  **VerticalDivider**の高さが0なので、アプリに表示されない理由がわかる。
2. このように、flexを1に切り替える。`Expanded`パッケージ化する`VerticalDivider`なぜなら`Expanded`高さ制約の代わりに幅制約を提供する。
3. 次に、セパレータの高さを上げたボタンの高さまで伸ばそうとするかもしれないので、水平軸のアライメントをストレッチに設定してみよう。高さはまだ0なので、次のステップに進む。

3. 詳細ツリーで寸法と制約をチェックします。

詳細ツリーを使用して、行とその子を調べます。

1.  `VerticalDivider`最初の`renderObject`.constraintプロパティは、ウィジェットが幅も高さも制約されていないことを示す。**Layout Explorer**表示は一致している。しかし`extraitionalConstraints`下の例では、幅は20だが、高さはまだ制約されていない。幅は問題ないので、高さに集中しよう。
2. 親ウィジェットに移動する`Row`, `renderObject`,デバッグを楽しもう`Row`高さの制約もない。

なぜですか?

覚えておくべき最も重要なことは、制約は受け継がれるということです。

列は行に伝えます。

  1. コードに戻って修正してください。
class Example3 extends StatelessWidget {
 @override
 Widget build(BuildContext context) {
 return SizedBox(
 height: 50.0,
 child: Row(
 ...
 ),
 );
 }
}

VerticalDivider に高さを持たせるには、高さ制約を指定する必要があります。行を SizedBox で囲み、高さを 50.0 に固定します。こうすることで、Row が VerticalDivider に高さ制約を渡すようになります。ホット リロード。VerticalDivider が画面にポップアップします。

補足:VerticalDividerの動作はListViewの動作とは異なります。高さを選択するように指示された場合、ListViewは可能な限り高く、VerticalDividerは可能な限り短くしたいのです。しかし、アプリケーションで正しく表示されるためには、両方とも高さの制約が必要です!

では、これら3つの例で修正したコードをColumn()の中にまとめてみましょう:

Column(
 children: [
 // Modify code here
 Example1(),
 Example2(),
 Example3(),
 ],
)

ホットリロードおめでとうございます!

まとめ

このチュートリアルで、あなたは学びます。

  • 制約は、ウィジェットツリーを通して下に渡されます。
  • Expandedは、行または列の子に対する制約を提供します。
  • Flutter Inspectorは、レイアウトの問題を扱うときの強い味方です。

詳しくはflutter.devのご覧ください。

デバッグを楽しんでください!

Read next

Echarts 共通 API

通常、プロジェクトに echarts を導入すると、グローバル echarts オブジェクトを取得できます。 echarts インスタンスを作成すると、echarts インスタンスが返されます。1つのコンテナに複数の echarts インスタンスを作成することはできません。 インスタンスを破棄します。破棄したインスタンスは再利用できません。 Dom コンテナ上のインスタンスを取得します。 インスタンスの初期化時に指定するテーマを登録します。 登録解除...

Feb 6, 2020 · 8 min read