こんにちは。月謝袋アプリ開発の SINQWELL です。
今回は小ネタですが、モバイルアプリなら必ず一度はぶつかる
「ソフトウェアキーボードがUIを押しつぶす」問題の話です。
1回目の修正で失敗して、2回目で正解にたどり着いたので、その過程ごと書きます。
起きていたこと
月謝袋アプリには、発表会費などを複数の生徒へまとめて登録するダイアログがあります。
中身は上から「項目名の入力欄」「金額の入力欄」「生徒のチェックリスト」。

生徒を先にチェックしてから項目名を入力する分には問題ないのですが、
項目名を先に入力すると——キーボードがせり上がってきて、こうなります。

生徒リストが1行分。スクロールもままならず、チェックどころではありません。
なぜ潰れるのか
当時のレイアウトはこうでした。
content: SizedBox(
height: 520, // 画面の62%を上限にした固定高さ
child: Column(
children: [
TextField(...), // 項目名
TextField(...), // 金額
Expanded( // 生徒リスト(残り全部)
child: ListView(...),
),
],
),
)
一見まともですが、キーボードが出るとFlutterのダイアログは
キーボードを避けて利用可能な高さが縮みます。固定サイズの入力欄は縮まないので、
しわ寄せは全部 Expanded へ。つまり 「固定高さ + Expanded」の組み合わせは、
ダイアログが縮んだとき Expanded の領域だけが潰れる構造なんです。
修正1回目: スクロール化 →「幾分まし」止まり
まず、固定高さをやめて全体をスクロール可能にし、
生徒リストには一定の高さを確保して内部スクロールにしました。
content: SingleChildScrollView(
child: Column(
children: [
TextField(...),
TextField(...),
SizedBox(
height: listH, // 画面の35%を200〜320pxにclamp
child: ListView(...), // リストは自分の中でスクロール
),
],
),
)
これで「潰れて操作不能」は解消。ただ、実機で試すと——
キーボードが画面の下半分を占領している以上、見える範囲は1〜2行。
スクロールはできるけれど快適とは言えない。「幾分ましになったような…」が
率直な感想でした。レイアウトをどういじっても、キーボードが出ている限り
場所が足りないという物理的な事実は変わりません。

修正2回目: 問題はレイアウトではなく「キーボードの閉じ方」だった
ここで視点を変えます。項目名を打ち終えて生徒リストに指を伸ばした時点で、
キーボードはもう用済みのはずです。なのにFlutterのTextFieldは、
ダイアログ内だと外をタップしてもフォーカスが残り、キーボードが居座ります。
答えは Flutter 3.7 で入った onTapOutside でした。
TextField(
controller: _labelC,
// 入力欄の外をタップしたらキーボードを閉じる
onTapOutside: (_) =>
FocusManager.instance.primaryFocus?.unfocus(),
...
)
たったこれだけ。項目名を入力して、生徒リストに触れた瞬間にキーボードが引っ込み、
ダイアログが元の大きさに戻って全員が見えます。
まとめ
- ダイアログ内の「固定高さ + Expanded」は、キーボードでダイアログが縮んだとき
Expanded だけが潰れる。潰れて困る領域は「固定高さ + 内部スクロール」にして、
全体をスクロールで逃がす(これは守りの修正)。 - でも本命は
onTapOutsideでキーボードをさっさと閉じること。
「次の操作に移ったらキーボードは用済み」という、ユーザーの頭の中の動きに
UIを合わせるのが正解でした。

レイアウトの工夫でスペースを捻出するより、そもそもキーボードに退場してもらう。
小さな話ですが、同じ問題で悩んでいる方の参考になれば嬉しいです。
その他の小さい修正
月謝袋アプリでは、生徒情報の画面から電話・メールをワンタップで起動できる機能も
同時に追加しました(v1.22.3、審査通過後に配信されます)。

どんな小さなことでも構いません。ここが読みづらい、見えにくい、ちょっとした不便など、お気軽にお知らせくださいね。


