概要
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
今回は以上です。