🐽

Flutterでキーボード表示した時に画面スクロールできるように

2022/06/04に公開

Before

↓のようなformの画面で、キーボード表示した時に、レイアウト崩れることがあります。

before

例えばコードはこういう感じ:

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text(widget.title),
    ),
    body: Padding(
      padding: const EdgeInsets.all(20),
      child: Column(children: [
        Container(
          height: 300,
          color: Colors.grey,
        ),
        Expanded(
          child: Form(
              child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              TextFormField(
                decoration: const InputDecoration(hintText: 'Username'),
              ),
              TextFormField(
                decoration: const InputDecoration(hintText: 'Password'),
              ),
            ],
          )),
        ),
        ElevatedButton(
          style: ElevatedButton.styleFrom(
            primary: Colors.blue,
            minimumSize: const Size.fromHeight(44), // NEW
          ),
          child: const Text('Log in'),
          onPressed: () {},
        ),
        const SizedBox(
          height: 34,
        )
      ]),
    ),
  );
}

Goal

  • レイアウト崩れない
  • キーボード表示時画面全体スクロールできるように(よくあるUI)

After

How

画面の要素を全部SizedBoxに入れ、SingleChildScrollViewでラップします。
SizedBoxのheightはcontextの高さからapp barの高さを引いて設定します。

Scaffold.of(context)そのまま使うと、contextはScaffoldをinitしたcontextになり、Scaffoldのchildのcontextではないため、exceptionが出ます。
そのため、Builderでラップします。

Widget build(BuildContext context) {
  return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Builder(
          builder: ((context) => SingleChildScrollView(
                child: SizedBox(
                  height: MediaQuery.of(context).size.height -
                      (Scaffold.of(context).appBarMaxHeight ?? 0),
                  child: Padding(
                    padding: const EdgeInsets.all(20),
                    child: Column(children: [
                      Container(
                        height: 300,
                        color: Colors.grey,
                      ),
                      Expanded(
                        child: Form(
                            child: Column(
                          mainAxisAlignment: MainAxisAlignment.center,
                          children: [
                            TextFormField(
                              decoration:
                                  const InputDecoration(hintText: 'Username'),
                            ),
                            TextFormField(
                              decoration:
                                  const InputDecoration(hintText: 'Password'),
                            ),
                          ],
                        )),
                      ),
                      ElevatedButton(
                        style: ElevatedButton.styleFrom(
                          primary: Colors.blue,
                          minimumSize: const Size.fromHeight(44), // NEW
                        ),
                        child: const Text('Log in'),
                        onPressed: () {},
                      ),
                      const SizedBox(
                        height: 34,
                      )
                    ]),
                  ),
                ),
              ))));
}

Discussion