最近Flutterで開発していて、A RenderFlex overflowed by ... エラーが頻発しました。
原因は、Page役割を担う大きなウィジェットでScaffoldを使っており、bodyパラメータにColumnを使って複数のウィジェットを配置していたことです。通常、Columnにはコードの可読性と再利用性のために、一部のウィジェットをファイルに分けて作成します。あるウィジェットはExpandedで囲んだColumnを持っている状態でした。このエラーが発生する前の階層構造は以下の通りです。
Scaffold
SafeArea
Column
Padding
SizedBox
Expanded
Form
Column
Expanded
Padding
Column
ListTile
SizedBox
Column
TextFormField
SizedBox
Text
SizedBox
Wrap
ChoiceChips
Container
FilledButton
ここで仮想キーボードが表示されると問題が発生しました。heightが小さい場合、Wrapウィジェットが囲んでいるChipウィジェットのうち下部にあるものとFilledButtonが互いに衝突するという問題でした。
当時の状況を再現した例です。


この問題を解決する方法として望んでいたのは、既に「次へ」ボタンがキーボードの上にうまく表示されていたので、仮想キーボードが表示される際にChipだけをスクロールできるようにすることでした。
多くの例を調べましたが、大半はScaffoldのbodyをSingleChildScrollViewで囲んでreverse: trueにすれば解決する、あるいはScaffoldにresizeToAvoidBottomInset: falseを設定すれば良いと言っていましたが、これらの方法には問題がありました。
SingleChildScrollViewでbodyを囲む場合は全体領域がスクロールされるため、固定したい領域も一緒にスクロールされてしまう問題がありました。必要な部分だけスクロールさせたかったので、この状況では良い方法ではないと思いました。
resizeToAvoidBottomInset: falseを使う場合はFilledButtonが仮想キーボードに隠れて見えなくなります。そうなるとScaffoldのbottomSheetを使う必要がありますが、bodyにSafeAreaを設定している状況でもiPhoneでは下部にあるHome indicatorの位置にボタンが生成され、SafeAreaが適用されませんでした。Paddingを付ければ解決しますが、仮想キーボードが表示される際にもその分パディングが付いてしまい、満足できませんでした。
ここでChipウィジェットをスクロールさせるためにLayoutウィジェットを追加しましたが、ファイルが複数つながっており、ColumnにColumnが入っている状況で、何かを追加してテストするたびにRenderFlex overflow関連のエラーが出て、コーディングが本当に大変でした。
このエラーが発生した最大の理由は、Column内部でExpandedで囲んだColumnがあり、そのColumn内でさらにExpandedを使うColumnがあったことです。このレイアウト構成をきちんと理解せず、考慮せずに作ってしまったため、エラーの原因を見つけるのが難しかったです。結局、既存のウィジェットをコメントアウトして、簡単な例を作りながらレイアウトを再構成しました。
Expandedは、Row、Column、Flexなどのウィジェットで、そのウィジェットが持つ主軸方向に伸ばせる長さだけ伸ばすために使うウィジェットです。
Row、Column、Flexの子として使用する必要があります。他の子が特定の部分を埋めると、Expandedウィジェットが残りの部分をすべて埋めます。
いろいろ試行錯誤した結果、必要なChipウィジェットだけスクロールされつつRenderFlex overflowが発生しない方法を見つけました。
Scaffold
SafeArea
Column
Padding
SizedBox
Expanded
Form
Column
Padding
Column
ListTile
SizedBox
Column
TextFormField
SizedBox
Text
SizedBox
Expanded
SingleChildScrollView
Wrap
ChoiceChips
Container
FilledButton
これだけが方法ではないかもしれませんが、この状況ではColumn内部で特定の領域をスクロール可能にするには、ExpandedとSingleChildScrollViewウィジェットを使えばよいです。
I'm selfish, impatient and a little insecure. I make mistakes, I am out of control and at times hard to handle. But if you can't handle me at my worst, then you sure as hell don't deserve me at my best.
— Marilyn Monroe