【Flutter】キーボードの表示切り替えを捕捉する

  • 2024年6月20日
  • 2024年6月20日
  • 小物

対象者

  • Flutterを使用している中級開発者
  • キーボードの表示・非表示の切り換えを検知する方法を知りたい方

はじめに

モバイルアプリケーションの開発において、ユーザーがテキスト入力を行う際にキーボードの表示・非表示を検知することは、UIの最適化やアプリの挙動を改善するために非常に重要です。今回は、Flutterを使用してキーボードの状態を捕捉する方法について説明します。

キーボードの有無で画面レイアウトを変えようと、TextFieldへのフォーカス有無で判断しようとしましたが、フォーカスが合っても、キーボードがない状態があったので、今回の方法でやり直し

キーボードの捕捉方法

WidgetsBindingObserver の設定

WidgetsBindingObserver は、Flutterのウィジェットライフサイクルやその他の重要なイベントを監視するためのオブザーバーです。キーボードの表示・非表示の状態を検知するのにも使えます。

以下のコードスニペットでは、MyHomePage ステートフルウィジェットの中で WidgetsBindingObserver を設定し、キーボードの状態を検知する方法を示しています。
キーボードの表示・非表示の状態が切り替わると、didChangeMetrics()が実行されます。

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
  }
}

キーボード有無の判定

didChangeMetrics メソッド内で、MediaQuery.of(context).viewInsets.vertical を使用してキーボードの表示状態を検知します。viewInsets.vertical が0より大きい場合、キーボードが表示されていると判断し、その結果を _isKeyboardVisible 変数に格納します。この変数の変更を監視し、UIを更新します。

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  var _isKeyboardVisible = false;

  @override
  void didChangeMetrics() {
    final newValue = 0 < MediaQuery.of(context).viewInsets.vertical;
    if (newValue != _isKeyboardVisible) {
      setState(() {
        _isKeyboardVisible = newValue;
      });
    }
  }
}

このコードにより、キーボードの表示・非表示の状態をリアルタイムで検知し、UIに反映させることができます。

Q&A

Q1: Flutterでキーボードの表示・非表示を検知する方法は何ですか?

A1: Flutterでは、WidgetsBindingObserverを使用してキーボードの表示・非表示を検知できます。具体的には、didChangeMetricsメソッド内でMediaQuery.of(context).viewInsets.verticalをチェックし、0より大きい場合はキーボードが表示されていると判断します。これにより、リアルタイムでキーボードの状態をUIに反映させることが可能です。

Q2: MediaQuery.of(context).viewInsets.vertical が0になります

おそらくWidget Tree(多分正確には Element Tree)上にMediaQueryが複数あり、キーボードのサイズと紐付いていないMediaQueryを取得していると思われます。私の場合、以下でScaffoldを経由すると、取得できました。お試しください。

0 < MediaQuery.of(Scaffold.of(context).context).viewInsets.bottom

Q3: didChangeMetricsは本来何をするメソッドですか?

A3: didChangeMetricsは、FlutterのWidgetsBindingObserverインターフェースの一部で、アプリケーションのウィジェットのサイズや位置に関する変更を検知するために呼び出されるメソッドです。このメソッドは、キーボードの表示・非表示、デバイスの回転、画面サイズの変更など、ビューに影響を与えるメトリクスの変化が発生した際にトリガーされます。これにより、動的にUIを更新するためのフックを提供します。

まとめ

Flutterでキーボードの表示・非表示を検知する方法について説明しました。WidgetsBindingObserver を使用することで、キーボードの状態を簡単に検知でき、ユーザーエクスペリエンスを向上させることができます。ぜひ、アプリケーションの開発に役立ててください。

参考

ソース(main.dartにコピペして動作確認用)

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

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

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> with WidgetsBindingObserver {
  var _isKeyboardVisible = false;

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this);
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    final newValue = 0 < MediaQuery.of(context).viewInsets.vertical;
    if (newValue != _isKeyboardVisible) {
      setState(() {
        _isKeyboardVisible = newValue;
      });
    }
    //   0 < MediaQuery.of(Scaffold.of(context).context).viewInsets.bottom
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(8.0),
          child: Column(
            children: [
              Text('Keyboard is visible: $_isKeyboardVisible'),
              TextField(),
              TextField(),
            ],
          ),
        ),
      ),
    );
  }
}