クリックできる目次
はじめに
こんにちは。私はFlutterでColumnウィジェットをよく使うのですが、
キーボードを開いた時に、コンテンツが隠れないように実装する時など、Columnの親にSingleChildScrollViewなどを設定したい時が出てきました。
しかし実際にレイアウトするとエラーが発生してしまいます。
その解決方法を書いてみたので、参考になれば幸いです。
※この悩みって私だけで、レイアウトの組み方が間違えてるのかな…?何かより良い方法があればコメントなどで教えていただけると嬉しいです。
通常のColumn Expanded

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
</code>body: Column( children: [ Container( height: 100, color: Colors.grey, ), Expanded( child: Container( color: Colors.cyan, ), ), Container( height: 100, color: Colors.grey, ), ], ), |
灰色の部分が固定値、水色の部分がExpandedで画面いっぱいまで広がるようになっています。
親がSingleChildScrollViewのColumn Expanded

|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
body: SingleChildScrollView( child: Column( children: [ Container( height: 100, color: Colors.grey, ), Expanded( child: Container( color: Colors.cyan, ), ), Container( height: 100, color: Colors.grey, ), ], ), ), |

このようにエラーが出てしまいます。その理由は以下が考えられます。
- SingleChildScrollViewはコンテンツの高さに明示的な上限がない
- Columnもコンテンツによって高さが決まるので明示的な高さがない
よってVertical方向の高さが決まってない状態でのExpandedがどこまで広がればいいのか分からない
解決方法
IntrinsicHeightを利用する(非推奨)

LayoutBuilderとIntrinsicHeightを利用するパターン。
しかし、Flutterの公式ドキュメントを見ると、このIntrinsicHeightはレイアウトの計算コストが高いようで、できる限り利用しないとの記述がありました。
参考: https://api.flutter.dev/flutter/widgets/IntrinsicHeight-class.html
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
body: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { return SingleChildScrollView( child: ConstrainedBox( constraints: BoxConstraints(minHeight: constraints.maxHeight), child: IntrinsicHeight( child: Column( children: [ Container( height: 100, color: Colors.grey, ), Expanded( child: Container( color: Colors.cyan, ), ), Container( height: 100, color: Colors.grey, ), ], ), ), ), ); }), |
SliverFillRemainingを利用する(個人的にオススメ)

CustomScrollViewとSliverFillRemainingを利用するパターン。
Sliversにその他スクロール可能領域を持つウィジェットを含めることができるようです。
またiOSのScrollViewやTableViewのように、SingleChildScrollViewはコンテンツがScrollView自体を超えない限りは上下に触っても動きません。
しかしCustomScrollViewはデフォルトでコンテンツがScrollView自体を超えなくとも動いてしまうので、physics: BouncingScrollPhysics() を指定しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
body: CustomScrollView( // コンテンツの高さがScrollViewを超えた時のみスクロールするようにする physics: BouncingScrollPhysics(), slivers: [ SliverFillRemaining( hasScrollBody: false, child: Column( children: [ Container( height: 100, color: Colors.grey, ), Expanded( child: Container( color: Colors.cyan, ), ), Container( height: 100, color: Colors.grey, ), ], ), ) ], ), |
おわりに
なんとかScrollViewの中にColumnのExpandedを入れることができました。同じレイアウトでも組み方が何通りもあって、もしかしたらScrollViewの中にColumnを入れることはベストプラクティスでは無いのかもしれませんが、なんとか実装出来て良かったです。
見て頂いてありがとうございます!
木の向くままのエンジニア 
