はじめに
現在、方言を話すおしゃべり猫型ロボット「ミーア」を開発中。
Flutterで、下記のようなRenderFlex overflowed
エラー」が生じた時の、デバッグ方法と対応法を記載。
RenderFlex overflowedエラーとは何か?
端的にいうと「ウィジェットのサイズが画面範囲を超えたときに起こるエラー」のこと。
Flutterのドキュメントに、最も頻繁に発生するエラーの 1 つとして記載されている。
https://docs.flutter.dev/testing/common-errors
これが発生すると、黄色と黒の縞模様が表示され、アプリ UI のオーバーフロー領域を示す。さらに、デバッグ コンソールにエラー メッセージが表示される。
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
The overflowing RenderFlex has an orientation of Axis.vertical.
The edge of the RenderFlex that is overflowing has been marked in the rendering with a yellow and
black striped pattern. This is usually caused by the contents being too big for the RenderFlex.
Consider applying a flex factor (e.g. using an Expanded widget) to force the children of the
RenderFlex to fit within the available space instead of being sized to their natural size.
This is considered an error condition because it indicates that there is content that cannot be
seen. If the content is legitimately bigger than the available space, consider clipping it with a
ClipRect widget before putting it in the flex, or using a scrollable container rather than a Flex,
like a ListView.
The specific RenderFlex in question is: RenderFlex#09969 relayoutBoundary=up5 OVERFLOWING:
needs compositing
creator: Column ← Padding ← Center ← Listener ← _GestureSemantics ← RawGestureDetector ←
GestureDetector ← DebugContainer ← HomeTab ← DownloadingOverlayWidget ← DeviceShadowListener ←
KeyedSubtree-[GlobalKey#1b409] ← ⋯
parentData: offset=Offset(32.0, 32.0) (can use size)
constraints: BoxConstraints(0.0<=w<=350.0, 0.0<=h<=740.0)
size: Size(350.0, 740.0)
direction: vertical
mainAxisAlignment: spaceBetween
mainAxisSize: max
crossAxisAlignment: center
verticalDirection: down
◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤◢◤
Flex(例えば、Column
やRow
)内の子ウィジェットが親ウィジェットが提供する空間より大きくなった場合に発生する。この問題は、ウィジェットツリーが適切にスペースを管理していないことを示している。
Flutter DevToolsのInspectorを使用して、問題を特定する。
flutter runでアプリを実行すると、実行直後にターミナルで、Dart VM ServiceとFlutter DevToolsのための二つのURLが表示される。
Flutter Inspector(ウィジェットツリーの視覚的分析と修正)に関しては、Dart VMではなく、Flutter Devtoolsが提供しているので、2つ目のURLをクリックする。
Flutter run key commands.
r Hot reload. 🔥🔥🔥
R Hot restart.
h List all available interactive commands.
d Detach (terminate "flutter run" but leave application running).
c Clear the screen
q Quit (terminate the application on the device).
A Dart VM Service on KazutakaのiPhone is available at: http://127.0.0.1:51945/XXXXXXXXXX/
The Flutter DevTools debugger and profiler on KazutakaのiPhone is available at:
http://127.0.0.1:9101?uri=http://127.0.0.1:51945/XXXXXXXXXXX/
左から2つ目の「Flutter Inspector」を開くと、現在のウィジェットツリーの全体像が表示される。
上記のエラーメッセージに表示されたウィジェット(このケースではColumn
ウィジェット)をクリックして選択する。
ターミナルですでにエラーメッセージとしても表示されているが、Columnウィジェットの中の子ウィジェットの合計が1pxはみ出しているよ、とエラー出ている。
A RenderFlex overflowed by 1.00 pixels on the bottom.
コード修正
ちなみに今回のコード(トップ画面)はこちら。
class HomeTab extends ConsumerStatefulWidget {
const HomeTab({super.key});
@override
_HomeTabState createState() => _HomeTabState();
}
class _HomeTabState extends ConsumerState<HomeTab> {
// ...
@override
Widget build(BuildContext context) {
// 必要なプロバイダーの読み取りやウォッチはここに含める
return DebugContainer(
child: Center(
child: Padding(
padding: const EdgeInsets.all(32.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
// Overflowエラーを引き起こしている可能性のあるウィジェット
Align(
alignment: Alignment.topCenter,
child: Column(
children: [
// ... 他のウィジェット
// エラーが発生している可能性のあるTopMessageBarウィジェット
firmwareUpdate.when(
data: (response) {
if (response.message == "NEW_FIRMWARE_AVAILABLE") {
return TopMessageBar(
message:
"新しいミーアのバージョン(${response.newFirmwareVersion})がダウンロード可能です",
// ... TopMessageBarの他のプロパティ
);
} else {
return SizedBox.shrink();
}
},
// ... ローディングとエラーの状態
),
// ... 他のウィジェット
],
),
),
// ... 他のColumn内のウィジェット
],
),
),
),
);
}
}
今回の原因は、下記OTAアップデート機能の実装で、新しくFirmwareアップデートのアプリ通知用としてホーム画面のトップにTextウィジェットとして差し込んだことが原因。
Firmwareのアップデートがない場合には、今まで通り通知文言が表示されないので、ウィジェットオーバーフローのエラーが表示されないが、通知がある場合には、その分、子ウィジェットの合計値が大きくなり、親ウィジェット(今回はColumnウィジェット)の幅を超えてしまう。
今回の場合は、Columnウィジェットをさらに、DebugContainerウィジェットで囲み、上下左右に32pxのパディングを指定している。
padding: const EdgeInsets.all(32.0),
なので、まだコンテンツの縦幅には余裕があるので、Bottomの32pxを0pxに変更することで対応してみる。
EdgeInsets
はFlutterでマージンやパディングを設定する際に使われるクラス。fromLTRB
はそのクラスのファクトリメソッドの一つで、それぞれの引数に左(left
)、上(top
)、右(right
)、下(bottom
)のマージンやパディングをピクセル単位で指定できる。
https://api.flutter.dev/flutter/painting/EdgeInsets/EdgeInsets.fromLTRB.html
padding: const EdgeInsets.fromLTRB(32.0, 32.0, 32.0, 0.0),
動作確認と今後の対応
無事、オーバーフローのエラーが解消できた。
今回は、bottomの調整だけで、エラー解消できたが、今後トップ画面に表示したいコンテンツが増えてきた場合の対応も考えておく必要がある。
今回のケースの場合は
- Snackbarを使用して、他のウィジェットの上に新しいウィジェットレイヤーを表示する。ただSnackbarは画面下部に表示される
Overlay
クラスを使用して、他のウィジェットの上に新しいウィジェットレイヤーを表示する。Column
ウィジェットの子ウィジェットにExpanded
やFlexible
を適用して、利用可能なスペースに合わせてウィジェットが拡大・縮小されるようにするSingleChildScrollView
を使用して、Column
ウィジェットをラップし、ユーザーがコンテンツをスクロールして全てを見られるようにする
などが考えられる。
ただ、今回の場合は、twitterやtiktokみたいに、コンテンツがたくさんあってスクロールによるタイムライン表示が必要というわけではないので、スクロールという手段はUIUXの観点からよくない。
Snackbarは画面下部なので気づきづらいかなと思うので、Overlayクラスを使用する予定。少し調べた限りだが、overlay_supportという、トーストやアプリ内通知を簡単に作成できるパッケージがあった。
コメント