Flutterで仮想キーボードによるRenderFlex overflowを解決する

 ・ 2 min

photo by Julian Rösner on Unsplash

最近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が互いに衝突するという問題でした。

当時の状況を再現した例です。

|320|320

この問題を解決する方法として望んでいたのは、既に「次へ」ボタンがキーボードの上にうまく表示されていたので、仮想キーボードが表示される際に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


他の投稿
強大国の興亡 커버 이미지
 ・ 3 min

強大国の興亡

キーチェーンパスワードポップアップエラーの解決方法 커버 이미지
 ・ 1 min

キーチェーンパスワードポップアップエラーの解決方法

営業秘密とは 커버 이미지
 ・ 1 min

営業秘密とは