狠狠撸

狠狠撸Share a Scribd company logo
FlutterでScrollViewとExpandedを併用し
てSignIn / SignUp画面 などの
レイアウトを作成する
Mobile Act Online #6
2021/10/15 Hironobu Iga
自己紹介
Hironobu Iga
Software Engineer
Twitter @iganin_dev
アジェンダ
● 想定するケースと挙動
● 発生する問題
● 対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
● 対処法2と問題 - SingleChildScrollView
● 対処法3 - IntrinsicHeight
アジェンダ
● 想定するケースと挙動
● 発生する問題
● 対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
● 対処法2と問題 - SingleChildScrollView
● 対処法3 - IntrinsicHeight
想定するケース
SignIn画面やSignUp画面でよくある画面構成
● 大きめのロゴ or 画像
● 入力フォーム
○ メールアドレス
○ パスワード
○ etc (画面要件で変動)
● 余白
● 画面下部固定のボタン
想定するケース
Scaffold(
body: Column(
children: [
Logo(), TextField(), TextField(), Spacer(), Button(),
]
)
);
想定するケースの挙動
TextFieldにフォーカスが当たると、
キーボードが表示され、
Scaffoldのbody内部のWidgetがその分圧縮される
余白が十分にあれば問題はない
アジェンダ
● 想定するケースと挙動
● 発生する問題
● 対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
● 対処法2と問題 - SingleChildScrollView
● 対処法3 - IntrinsicHeight
発生する問題
余白が十分にないとキーボード分圧縮されたことで、
各Widgetを配置するために必要なスペースが確保できず、
画面表示が崩れる
※Releaseビルドであれば、黄色の帯のような表示はないが、
レイアウトが崩れてしまう
アジェンダ
● 想定するケースと挙動
● 発生する問題
● 対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
● 対処法2と問題 - SingleChildScrollView
● 対処法3 - IntrinsicHeight
対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
resizeToAvoidBottomInset
Scaffoldの設定値で、 true だとキーボードとScaffoldのbodyの
中身が被らないようにbodyの中身の高さを調整してくれる
デフォルトはtrue
対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
Scaffold {
resizeToAvoidBottomInset: false,
body: ...
}
ScaffoldのresizeToAvoidBottomInsetをfalse
にすることでひとまずの対処は可能
対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
問題も発生する
キーボードがbodyに被さってしまうことで、
TextFieldが見えなくなるようなことがある
入力値を確認できずUXを損なう
アジェンダ
● 想定するケースと挙動
● 発生する問題
● 対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
● 対処法2と問題 - SingleChildScrollView
● 対処法3 - IntrinsicHeight
対処法2と問題 - SingleChildScrollView
SingleChildScrollView(
child: Column(
children: [...]
)
)
検索するとよく出てくる
ScrollViewないでキーボードの高さ分を調整してく
れる
多くの場合、この方法で問題ない
ただし、Columnのchildrenに
SpacerやExpandedなどを含むと
レイアウトが崩れる
対処法2と問題 - SingleChildScrollView
RenderBox was not laid out:
RenderRepaintBoundary#f85cc
relayoutBoundary=up1 NEEDS-PAINT
'package:flutter/src/rendering/box.dart':
Failed assertion: line 1930 pos 12:
'hasSize'
例えば左のような例外が発生する
SpacerやExpandedは可能な限りスペースを取ろう
とする
SingleChildScrollViewは内部の高さから全体の高
さを決める
したがって、高さが不定となりレイアウトが崩れる
対処法2と問題 - SingleChildScrollView
今回のレイアウトは画面下部にボタンを固定したい
Spacerなどで余白を埋めたいため、
SingleChildScrollViewによる方法は使用できない
アジェンダ
● 想定するケースと挙動
● 発生する問題
● 対処法1と問題 - ScaffoldのresizeToAvoidBottomInset
● 対処法2と問題 - SingleChildScrollView
● 対処法3 - IntrinsicHeight
対処法3 - IntrinsicHeight
実は今回のケースに対する対応法は公式ドキュメントに書かれている
Simply doing so, however, usually results in a conflict between the Column,
which typically tries to grow as big as it can, and the SingleChildScrollView,
which provides its children with an infinite amount of space.To resolve this
apparent conflict, there are a couple of techniques, as discussed below.
https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
対処法3 - IntrinsicHeight
簡単な要約
ColumnにExpandedなどが含まれると可能な限りのスペースを取得しようとする。
ScrollViewはChildに無限のSpaceを与える。
したがって、Conflictが起きるが以下の方法で解決できる。
対処法3 - IntrinsicHeight
方法
● LayoutBuilder
● IntrinsicHeight
を併用する
対処法3 - IntrinsicHeight
LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
return Widget();
}
)
親Widgetでとりうる最大の高さなどをconstraintsとして取得できる
対処法3 - IntrinsicHeight
IntrinsicHeight
Childに含んだColumnの高さを
Columnが含むchildrenの高さの合計値と同じにする
※Spacerなどによる余白を埋めるような高さは考慮されない
対処法3 - IntrinsicHeight 実装(主要部分のみ)
LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: IntrinsicHeight(....),
…... minHeightを親WidgetのmaxHeightに指定する
IntrinsicHeightのchildの高さが親Viewより小さい場
合でも内部の要素が親 View一杯に広がる
対処法3 - IntrinsicHeight 実装(主要部分のみ)
LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: IntrinsicHeight(....),
…... IntrinsicHeightを使用することで、
ScrollView内の高さが無限大になることを防ぐ
対処法3 - IntrinsicHeight 実装(主要部分のみ)
LayoutBuilder(
builder: (context, constraints) {
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(minHeight: constraints.maxHeight),
child: IntrinsicHeight(....),
…...
SingleChildScrollViewを使用することでキーボードの
問題に対処する
また、IntrinsicHeightの中身の高さが親Widgetより高
くなった場合のレイアウト崩れを防止
対処法3 - IntrinsicHeight
SingleChildScrollViewの内部で、LayoutBuilderと
ConstrainedBoxを使用し、minHeightを親Widgetの高さと
する
IntrinsicHeightを使用し、SpacerやExpandedがScrollView
の高さを無限大にしてしまうことを防ぐ
キーボード表示による画面レイアウト調整に対処しながら、
当初目的としていたレイアウトの作成を達成できた
対処法3 - IntrinsicHeight 注意点
IntrinsicHeightは計算コストが高い 最悪 O(N^2)
This class is relatively expensive, because it adds a speculative layout pass
before the final layout phase. Avoid using it where possible. In the worst
case, this widget can result in a layout that is O(N?) in the depth of the tree.
複雑なレイアウト構成ではできれば避けたい
References
● 公式ドキュメント
○ https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html
● GitHub Issue
○ https://github.com/flutter/flutter/issues/18711
● 以前書いたブログ記事
○ https://iganin.hatenablog.com/entry/2021/09/04/152752

More Related Content

Flutterて?scroll viewとexpandedを併用してsign in sign up画面 なと?の レイアウトを作成する