【Flutter】Flutter実装メモ showModalBottomSheetのカスタム

flutter

概要

Flutterで実装する時にどうやったっけ?をちょっと記録しておこうと思います。
今回は小画面を画面下から表示させるshowModalBottomSheetについてです。

よく見る画面の下方からニュルっと出てくる画面ですが、これが入力フォームだった場合に、ソフトウェアキーボードが表示された時に見えなくなってしまう場合の対応例です。

例えば以下のような場合、Passwordを入力しても結果が見えなくなってしまいます。

これを以下のように表示させます。

実装

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Column(
        children: [
          const Padding(
            padding: EdgeInsets.all(20.0),
            child: Text(
              'TOP-PAGE',
              style: TextStyle(fontSize: 24.0),
            ),
          ),
          Center(
            child: ElevatedButton(
              onPressed: () => showModalBottomSheet(
                context: context,
                isScrollControlled: true, // 画面半分よりも大きなモーダルの表示設定
                builder: (BuildContext context) {
                  return const ModalWindow();
                },
              ),
              child: const Text('SIGN IN'),
            ),
          ),
        ],
      ),
    );
  }
}

class ModalWindow extends StatefulWidget {
  const ModalWindow({super.key});

  @override
  State<ModalWindow> createState() => _ModalWindowState();
}

class _ModalWindowState extends State<ModalWindow> {
  bool isDispayKeybord = false;
  final FocusNode _focusUserId = FocusNode();
  final FocusNode _focusPassword = FocusNode();

  @override
  void initState() {
    super.initState();
    _focusUserId.addListener(() => _onFocusChange(_focusUserId));
    _focusPassword.addListener(() => _onFocusChange(_focusPassword));
  }

  void _onFocusChange(FocusNode focus) {
    setState(() {
      if (focus.hasFocus) {
        isDispayKeybord = true;
      } else {
        isDispayKeybord = false;
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: isDispayKeybord // キーボードの有無により、モーダルのサイズを設定
          ? MediaQuery.of(context).size.height * 0.8
          : MediaQuery.of(context).size.height * 0.5,
      child: Column(
        children: [
          const Padding(
            padding: EdgeInsets.all(20.0),
            child: Text(
              'SIGN-IN-FORM',
              style: TextStyle(fontSize: 24.0),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(20.0),
            child: TextField(
              focusNode: _focusUserId,
              decoration: const InputDecoration(
                labelText: 'USER-ID',
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(10.0)),
                ),
              ),
            ),
          ),
          Padding(
            padding: const EdgeInsets.all(20.0),
            child: TextField(
              focusNode: _focusPassword,
              decoration: const InputDecoration(
                labelText: 'PASSWORD',
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.all(Radius.circular(10.0)),
                ),
              ),
            ),
          ),
          ElevatedButton(
            onPressed: () => debugPrint('SIGN-IN'),
            child: const Text('SIGN-IN'),
          ),
        ],
      ),
    );
  }
}

先にコードを出しましたが、基本的にモーダルは画面の半分以上のサイズにはなりません。

モーダルのサイズを大きく設定したい場合は、呼び出し側のshowModalBottomSheetメソッドのisScrollControlledプロパティを設定し、モーダル側のWidgetでサイズを指定します。

         ElevatedButton(
              onPressed: () => showModalBottomSheet(
                context: context,
                isScrollControlled: true, // 画面半分よりも大きなモーダルの表示設定
                builder: (BuildContext context) {
                  return const ModalWindow();
                },
              ),
              child: const Text('SIGN IN'),
            ),

これと一緒に、テキストフィールドにフォーカスがある場合、キーボードの高さを分を逃すような設定を行なっています。

実装によっては以下のコードなどでキーボードの出現を検知するなどの方が対応しやすい場合もあると思います。

MediaQuery.of(context).viewInsets.bottom

今回は以上です。

関連記事

  1. 【Widget of the Week】#2 Expanded

  2. flutter

    【Flutter】画面が開く前にローディングを表示

  3. flutter

    【Flutter】KotlinでNative側を実装しFlutterと…

  4. flutter

    【Flutter】アプリ名やアイコンの変更とローンチスクリーンの表示

  5. flutter

    【Flutter】GoogleMapを使用したアプリの開発について P…

  6. flutter

    【Flutter】GoogleMapを使用したアプリの開発について P…

最近の記事

  1. AWS
  2. flutter

制作実績一覧

  1. Checkeys