Flutterレイアウトのビジュアルデバッグ機能

Flutterにはレイアウトを視覚的にデバッグするために、topレベルのプロパティとして、いくつかbooleanフラグが用意されています。
https://flutter.io/debugging/#visual-debugging

全てではありませんが、[Flutter Widget Inspector](https://flutter.io/inspector/)から設定できるフラグもあります。

<br/>

###debugPaintSizeEnabled

Widgetの境界線は明るい緑掛かった水色(teal)、パッドやマージンは薄いブルー(faded blue)で塗りつぶすとともに、パディングしたchildのWidgetの境界線はダークブルーでふち取っています。ドキュメントに記述はありませんが、ListViewの下に伸びた緑の矢印は表示する軸の方向を表しているようです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/09acff1c-6d1a-e19c-2f43-7774fa73ca09.png" width=33%>

mainAxisAlignmentなどのアラインメントは黄色い矢印で表しています。スクリーンショットにはありませんが、childのないContainerなどをスペーサーとして配置しているとグレーで表示されるようです。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/dd0ce17e-1878-1c52-4529-ab907cf5d10a.png" width=33%>

<br/>

###debugPaintBaselinesEnabled

debugPaintSizeEnabledと似ていますが、ベースラインが表示されます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/6eb831cb-9757-b667-e80b-7b1cee7ced29.png" width=33%>

> Flutter Widget InspectorのAdditional Actions>Show Paint Baselinesでも表示できます。

<br/>

###debugPaintPointersEnabled

タップした箇所が明るい緑掛かった水色(teal)で表示されます。ヒットテストに使用できます。

> スクリーンショットはタップできないので割愛！

<br/>

###debugPaintLayerBordersEnabled

レイヤの境界線をオレンジ色で表示します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/d36fba3f-3871-ba78-06f8-af27a7f775e5.png" width=33%>
<br/>
compositor(composited?) layersのデバッグに使用する、といわれても何のことだかさっぱりですが、つらつら読んだ限りではcomposited layer単位に再描画を行っているようで、出来合いのWidgetを使用している限りlayerは所与ですが、再描画を行う範囲を明示的にRepaintBoundaryでラップすることにより、再描画の伝播を必要最小限に留めることができるので、このデバッグフラグはその範囲を見極めるために使用する、ということのようです。

以下は[Debugging application layers](https://flutter.io/debugging/#debugging-application-layers)にある[debugDumpLayerTree()](https://docs.flutter.io/flutter/rendering/debugDumpLayerTree.html)でlogcat出力したレイヤーツリーですが、先ほどのスクリーンショットで境界分けされたレイヤごとにcreatorとしてRepaintBoundaryが載っており、確かにcomposited layerとRepaintBoundaryは一対一の関係にありそうです。

```:logcat
...snip...
I/flutter:  │     │ │ │   ├─child 1: OffsetLayer#99b87
I/flutter:  │     │ │ │   │ │ creator: RepaintBoundary-[<0>] ←
I/flutter:  │     │ │ │   │ │   NotificationListener<KeepAliveNotification> ← KeepAlive ←
I/flutter:  │     │ │ │   │ │   AutomaticKeepAlive ← SliverList ← MediaQuery ← SliverPadding ←
I/flutter:  │     │ │ │   │ │   Viewport ← _ScrollableScope ← IgnorePointer-[GlobalKey#109c4] ←
I/flutter:  │     │ │ │   │ │   Semantics ← Listener ← ⋯
I/flutter:  │     │ │ │   │ │ offset: Offset(0.0, 0.0)
I/flutter:  │     │ │ │   │ │
I/flutter:  │     │ │ │   │ └─child 1: PictureLayer#555dd
I/flutter:  │     │ │ │   │     paint bounds: Rect.fromLTRB(0.0, 0.0, 411.4, 240.0)
I/flutter:  │     │ │ │   │
I/flutter:  │     │ │ │   ├─child 2: OffsetLayer#6317d
I/flutter:  │     │ │ │   │ │ creator: RepaintBoundary-[<1>] ←
I/flutter:  │     │ │ │   │ │   NotificationListener<KeepAliveNotification> ← KeepAlive ←
I/flutter:  │     │ │ │   │ │   AutomaticKeepAlive ← SliverList ← MediaQuery ← SliverPadding ←
I/flutter:  │     │ │ │   │ │   Viewport ← _ScrollableScope ← IgnorePointer-[GlobalKey#109c4] ←
I/flutter:  │     │ │ │   │ │   Semantics ← Listener ← ⋯
I/flutter:  │     │ │ │   │ │ offset: Offset(0.0, 240.0)
I/flutter:  │     │ │ │   │ │
I/flutter:  │     │ │ │   │ └─child 1: PictureLayer#115df
I/flutter:  │     │ │ │   │     paint bounds: Rect.fromLTRB(0.0, 0.0, 411.4, 104.0)
I/flutter:  │     │ │ │   │
I/flutter:  │     │ │ │   ├─child 3: OffsetLayer#35a4c
I/flutter:  │     │ │ │   │ │ creator: RepaintBoundary-[<2>] ←
I/flutter:  │     │ │ │   │ │   NotificationListener<KeepAliveNotification> ← KeepAlive ←
I/flutter:  │     │ │ │   │ │   AutomaticKeepAlive ← SliverList ← MediaQuery ← SliverPadding ←
I/flutter:  │     │ │ │   │ │   Viewport ← _ScrollableScope ← IgnorePointer-[GlobalKey#109c4] ←
I/flutter:  │     │ │ │   │ │   Semantics ← Listener ← ⋯
I/flutter:  │     │ │ │   │ │ offset: Offset(0.0, 344.0)
I/flutter:  │     │ │ │   │ │
I/flutter:  │     │ │ │   │ └─child 1: PictureLayer#8e164
I/flutter:  │     │ │ │   │     paint bounds: Rect.fromLTRB(0.0, 0.0, 411.4, 70.0)
I/flutter:  │     │ │ │   │
I/flutter:  │     │ │ │   └─child 4: OffsetLayer#88221
I/flutter:  │     │ │ │     │ creator: RepaintBoundary-[<3>] ←
I/flutter:  │     │ │ │     │   NotificationListener<KeepAliveNotification> ← KeepAlive ←
I/flutter:  │     │ │ │     │   AutomaticKeepAlive ← SliverList ← MediaQuery ← SliverPadding ←
I/flutter:  │     │ │ │     │   Viewport ← _ScrollableScope ← IgnorePointer-[GlobalKey#109c4] ←
I/flutter:  │     │ │ │     │   Semantics ← Listener ← ⋯
I/flutter:  │     │ │ │     │ offset: Offset(0.0, 414.0)
I/flutter:  │     │ │ │     │
I/flutter:  │     │ │ │     └─child 1: PictureLayer#ac76c
I/flutter:  │     │ │ │         paint bounds: Rect.fromLTRB(0.0, 0.0, 411.4, 208.0)
...snip...
```

試しに、アイコンごとにRepaintBoundaryでラップしたところ、レイヤの境界線は以下のようになりました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/d7a08201-f8b6-0adc-f2eb-6cd94ef873bd.png" width=33%>

<br/>

###debugRepaintRainbowEnabled

こちらもdebugPaintLayerBordersEnabled同様にレイヤの境界線を太枠で表していますが、再描画のたびに色が変わっていきます。本来は再描画された境界線のみが変色していることを確認する機能だとは思いますが、Hot Reloadボタンを何回か押せば色が変わっていくのを手っ取り早く確認できます。

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/0b496c98-b00e-4904-d412-6922f61cda5d.png" width=33%>

> Flutter Widget InspectorのAdditional Actions>Enable Repaint Rainbowでも表示できます。

<br/>

Flutter Widget Inspectorから変更できるフラグもありますが、プログラム的にはmain()の先頭で設定します。

```dart:main.dart

import 'package:flutter/rendering.dart';

void main() {
  debugPaintSizeEnabled = true;
//  debugPaintBaselinesEnabled = true;
//  debugPaintPointersEnabled = true;
//  debugPaintLayerBordersEnabled = true;
//  debugRepaintRainbowEnabled = true;

  runApp(MyApp());
}
```

もちろんお望みなら、全部有効にすることも可能です。

<br/>

###debugShowMaterialGrid(MaterialApp)

MaterialAppには、[Material Design baseline grid](https://material.io/guidelines/layout/metrics-keylines.html)を表示するフラグが用意されています。
https://flutter.io/debugging/#material-grid

<img src="https://qiita-image-store.s3.amazonaws.com/0/49088/90fda5b2-774b-0abe-9f02-f88649e934df.png" width=33%>

こちらはMaterialAppのコンストラクタで指定します。

```dart
    return MaterialApp(
      debugShowMaterialGrid: true,
      ...snip...
    );
```

<br/>

ビジュアルデバッグ機能はうざいのでいずれオフると思いますが、Flutter全般で、debugほにゃららといったプロパティやメソッドはデバッグモードでしか効かないので、リリース時に残っていても無害です。

<br/>

他にも、便利なデバッグ機能があります。読んどいたらよろし。
https://flutter.io/debugging/