フォームを作成するとき TextFormField を用いて実装することが多いですが、キーボードによるテキスト入力ではなく、選択肢から選ばせたい場合があります。
独自のウィジェットを作成しても良いですが、 TextFormField として実装することで、他の TextFormField と同じように onSave()
を処理したり、バリデーションを適応することができます。
「テキストフィールドをタップすると、ピッカー(ドラムロール)が表示され、選択したものがフォームに入力される」というものを実装します。
まず普通のテキストフィールドを作成します。
TextFormField buildTextFormField(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(hintText: 'テキストを入力'),
);
}
onTap()
でタップした時にピッカーを呼び出します。
ピッカー部分の実装は以前の投稿を参照。
【Flutter】画面下から出てくるピッカー(ドラムロール)を実装する
TextFormField buildTextFormField(BuildContext context) {
return TextFormField(
onTap: () {
// キーボードが出ないようにする
FocusScope.of(context).requestFocus(new FocusNode());
showPicker();
},
decoration: const InputDecoration(hintText: 'テキストを入力'),
);
}
void showPicker() {
final list = ['選択肢1', '選択肢2', '選択肢3'];
final _pickerItems = list.map((item) => Text(item)).toList();
var selectedIndex = 0;
showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 216,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: CupertinoPicker(
itemExtent: 32,
children: _pickerItems,
onSelectedItemChanged: (int index) {
selectedIndex = index;
},
),
),
);
},
);
}
テキストフィールドをタップするとピッカーが起動するようになります。
そのままだとピッカーが出る前にキーボードが一瞬表示されてしまうので、
FocusScope.of(context).requestFocus(new FocusNode())
を入れて抑制しています。
ピッカーを閉じたとき、選択した文字列を TextFormField に反映します。
// コントローラーを追加
final TextEditingController _controller = TextEditingController();
TextFormField buildTextFormField(BuildContext context) {
return TextFormField(
onTap: () {
// ...
},
// コントローラーを追加
controller: _controller,
decoration: const InputDecoration(hintText: 'テキストを入力'),
);
}
showCupertinoModalPopup<void>(
// ...
).then((_) {
if (selectedIndex != null) {
_controller.value = TextEditingValue(text: list[selectedIndex]);
}
});
TextFormField として実装しているため、validator()
や onSave()
を実装することができます。
コード全体
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'TextFormField Picker',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MyHomePage(title: 'TextFormField Picker'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: buildTextFormField(context),
));
}
TextFormField buildTextFormField(BuildContext context) {
return TextFormField(
onTap: () {
// キーボードが出ないようにする
FocusScope.of(context).requestFocus(new FocusNode());
showPicker();
},
controller: _controller,
decoration: const InputDecoration(hintText: 'テキストを入力'),
);
}
void showPicker() {
final list = ['選択肢1', '選択肢2', '選択肢3'];
final _pickerItems = list.map((item) => Text(item)).toList();
var selectedIndex = 0;
showCupertinoModalPopup<void>(
context: context,
builder: (BuildContext context) {
return Container(
height: 216,
child: GestureDetector(
onTap: () {
Navigator.pop(context);
},
child: CupertinoPicker(
itemExtent: 32,
children: _pickerItems,
onSelectedItemChanged: (int index) {
selectedIndex = index;
},
),
),
);
},
).then((_) {
if (selectedIndex != null) {
_controller.value = TextEditingValue(text: list[selectedIndex]);
}
});
}
}