MENU

Flutterでキーボード表示時に画面が隠れないようにする方法|フォームをスクロールさせるコツ

Flutterでキーボード表示時に画面が隠れないようにする方法|フォームをスクロールさせるコツ

入力欄が1〜2個だけなら、Scaffoldの標準設定によってキーボード分だけ本文の表示領域が狭くなり、フォーカス中の部品が見える位置へ移動することがあります。Flutter公式APIでは、resizeToAvoidBottomInsetの既定値はtrueで、画面上のキーボードと重ならないように本文をリサイズする動作です。

ただし、入力欄が多いフォームや、縦に長いColumnをそのまま置いた画面では、縮んだ領域に内容が収まらず、下部の部品が見えないことがあります。その場合は、ColumnをSingleChildScrollViewで包み、キーボードが表示されたあとに上下へ移動できるようにします。

❓ 初心者の疑問
resizeToAvoidBottomInset: trueにすれば、どんな画面でも自動で直る?

自動調整は大切ですが、画面の内容が表示領域より長い場合、スクロールできる仕組みまでは作ってくれません。固定されたColumnのままでは、キーボードを避けるための空間ができても、下にあるボタンまで移動できないためです。

目次

キーボードで画面が隠れる主な原因

`Column`の内容が画面の高さを超えている

Columnは子ウィジェットを上から順番に並べますが、標準では画面を上下に動かす機能を持っていません。入力欄、説明文、ボタンを追加するほど、キーボード表示時に下側がはみ出しやすくなります。

`resizeToAvoidBottomInset`を無効にしている

次のようにfalseを指定すると、キーボードが表示されてもScaffoldの本文を縮めない設定になります。

Scaffold(
  resizeToAvoidBottomInset: false,
  body: ...,
)

この設定が必要な特殊なレイアウトもありますが、通常の入力フォームでは、まずtrueまたは指定なしで動作を確認するのが安全です。

📘 用語メモ
resizeToAvoidBottomInsetは、キーボードの表示領域を避けるためにScaffoldの本文やフローティング部品を縮める設定です。通常のフォームではtrueのまま使うのが基本です。

スクロール部品の中に入力欄が入っていない

SingleChildScrollViewを置いていても、TextFieldや送信ボタンがその外側にあると、必要な部品だけをスクロールできません。画面全体を動かしたい場合は、入力欄とボタンを同じスクロール領域に入れます。

基本対策:SingleChildScrollViewでフォームを包む

次の例は、名前とメールアドレスを入力して送信する、縦長になりやすいフォームです。ScaffoldのbodyにSafeAreaを置き、その中へSingleChildScrollViewとColumnを入れています。

import 'package:flutter/material.dart';

class ProfileFormPage extends StatelessWidget {
  const ProfileFormPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('プロフィール入力')),
      resizeToAvoidBottomInset: true,
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              const Text('必要な情報を入力してください'),
              const SizedBox(height: 24),
              TextField(
                decoration: const InputDecoration(
                  labelText: '名前',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 16),
              TextField(
                keyboardType: TextInputType.emailAddress,
                decoration: const InputDecoration(
                  labelText: 'メールアドレス',
                  border: OutlineInputBorder(),
                ),
              ),
              const SizedBox(height: 24),
              ElevatedButton(
                onPressed: () {},
                child: const Text('送信する'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

この構造なら、キーボードで表示領域が狭くなっても、指で上へスクロールして送信ボタンを表示できます。paddingをSingleChildScrollViewに設定しておくと、入力欄が画面端に密着せず、キーボード表示後も操作しやすくなります。

💡 ポイント
スクロールさせたい入力欄とボタンは、できるだけ同じSingleChildScrollViewの中へ入れます。外側に固定したボタンがある場合は、キーボードとの重なりを別に設計する必要があります。

画面がさらに長い場合の組み立て方

入力項目が多い場合は、単にTextFieldを増やすのではなく、次の順番で画面を組み立てると原因を切り分けやすくなります。

  1. ScaffoldのresizeToAvoidBottomInsetをtrueにする、または指定を外す
  2. bodyの中にSafeAreaを置く
  3. SafeAreaの中にSingleChildScrollViewを置く
  4. SingleChildScrollViewの中へColumnと入力欄、ボタンをまとめる
  5. 実機またはエミュレーターで、最も下にある入力欄をタップしてスクロールを確認する

フォームの入力チェックにFormやTextFormFieldを使う場合も、Formをスクロール領域の中へ置けます。検証処理はキーボード問題とは別の役割なので、画面が隠れる対策と入力チェックを混ぜすぎないようにしましょう。

うまく動かないときの確認ポイント

`ListView`や別のスクロール領域が重なっていないか

SingleChildScrollViewの中に、さらに高さが決まっていないListViewを入れると、レイアウトエラーやスクロールの競合が起きることがあります。長い入力フォームなら、まずはスクロール領域を1つに絞り、必要性が明確な場合だけ内側のリストを追加してください。

`resizeToAvoidBottomInset: false`が親画面に残っていないか

子ウィジェットだけを確認しても、親のScaffoldや共通レイアウトで設定を変更していると、意図した動きにならないことがあります。プロジェクト全体でresizeToAvoidBottomInsetを検索し、フォーム画面に影響する設定を確認します。

キーボードを閉じる操作も用意する

入力が終わったあともキーボードが残ると、ボタンが押しにくい場合があります。画面の空白部分をタップしたらフォーカスを外す設計にすると、確認操作がしやすくなります。

GestureDetector(
  onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
  child: SingleChildScrollView(
    child: ...,
  ),
)

FocusNodeを使って入力先を管理する場合は、StatefulWidgetのinitStateで作成し、disposeで破棄します。buildのたびに新しいFocusNodeを作ると、フォーカスが外れたり管理対象が増えたりする原因になります。

⚠️ 注意
GestureDetectorを追加しただけで、キーボードによる画面の重なりが解決するわけではありません。これはキーボードを閉じる補助であり、画面を動かす対策はScaffoldとスクロール領域で行います。

よくある失敗例と改善方法

✖ 失敗例
Columnの外側にSingleChildScrollViewを置いたつもりですが、送信ボタンだけが画面下に残って動きません。

原因は、ボタンがスクロール領域の外側にある構造です。入力欄とボタンを同じColumnへ入れ、そのColumn全体をSingleChildScrollViewで包みます。

✔ 改善例
Scaffoldの本文をSafeArea、SingleChildScrollView、Columnの順にし、フォームの部品をすべてColumnの子にまとめます。

もう一つの失敗は、画面下に固定したボタンをStackなどで重ね、キーボード表示時の余白を考慮しないケースです。固定ボタンが必要なら、キーボードの表示領域をMediaQuery.viewInsetsOf(context).bottomで確認し、その分だけ下側の余白を調整する設計を検討します。単純なフォームなら、固定せずにスクロール領域へ入れる方が保守しやすいでしょう。

まとめ:最初に確認するのは3か所

Flutter キーボード 画面の問題は、まずresizeToAvoidBottomInsetを無効にしていないか確認し、次にフォーム全体をSingleChildScrollViewへ入れます。それでも操作しにくければ、入力欄とボタンの配置、スクロール領域の重複、キーボードを閉じる操作を見直します。

👉 次にやること
手元のフォーム画面で一番下のTextFieldをタップし、キーボード表示後に入力欄と送信ボタンがスクロールで見えるか確認してください。

フォームの基本構造をまだ作っていない場合は、Flutterでフォーム入力を実装する方法もあわせて読むと、入力欄の作成と今回のキーボード対策を続けて確認できます。

参考にした公式情報

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

meibuのアバター meibu 中年ブロガー

【このブログを書いている人】
こんにちは、meibuです!
読者の「ちょっと気になる」「でも難しそう…」をやさしく解決したくてこのブログを始めました。

🔹プロフィールまとめ
◎ 本業は運送業の管理職
 → 現場からマネジメントまで幅広く経験

● 趣味はパソコン・ランニング・情報収集

●興味のあるテーマ:
 副業 / ChatGPT

● 発信スタイルは…
 → 「初心者にもわかりやすく」+「図解・たとえ話も使って解説」

● ブログの目的:
 日常に“ちょっと得する”選択肢を増やすこと
管理職の悩みを最近少し書いています

🔸こんな人に読んでほしい!
ChatGPTってなに?使い方がわからない方
管理職の職位に悩んでいる人
忙しいけど、自分の未来のために動きたい方

● 「読んでよかった!」と思える情報を、これからもコツコツ更新していきます!
当ブログはアフェリエイト広告を利用しています。

目次