[Flutter] TextFieldウィジェット
flutter目次
- 概要
- Flutterプロジェクト生成
- TextField
- InputDecoration
- SingleChildScrollView
- GestureDetectorとFocusScope
- TextFieldの値を使う方法
- onChanged
- TextEditingController
- ScaffoldのresizeToAvoidBottomInset
- 完了
概要
Flutterを使ってアプリを開発してみようかと思います。今回のブログポストではFlutterでユーザの入力を受ける方法について説明します。
このブログポストで紹介するソースコードは下記のリンクで確認できます。
- GitHub: https://github.com/dev-yakuza/study-flutter/tree/main/widget
Flutterプロジェクト生成
Flutterでユーザの入力を貰うためにはTextFieldウィジェットを使います。TextFieldウィジェットを使うためまず、Flutterのプロジェクトを生成します。
flutter create my_app cd my_appTextField
プロジェクトを生成したら、main.dartファイルを次のように修正してTextFieldを表示します。
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Demo', theme: ThemeData( primarySwatch: Colors.blue, ), debugShowCheckedModeBanner: false, home: Home(), ); } } class Home extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('TextField'), ), body: Center( child: Padding( child: TextField( decoration: InputDecoration( labelText: 'Input', ), ), padding: EdgeInsets.all(20.0), ), ), ); } }上のようにコードをセク生すると次のような画面が確認できます。
TextFieldを表示する部分を詳しくみてみましょう。
TextField( decoration: InputDecoration( labelText: 'Input', ), )上のようにTextFieldを表示することができるし、decorationパラメーターでInputDecorationを使って色んな設定ができます。
InputDecoration
InputDecorationを使うと色んな形のTextFieldウィジェットが使えます。
TextField( decoration: InputDecoration( labelText: 'Email', hintText: 'Enter your email', labelStyle: TextStyle(color: Colors.redAccent), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), ), ), keyboardType: TextInputType.emailAddress, )このようにInputDecorationを使うと下記のように色んなデザインを適用することができます。
SingleChildScrollView
TextFieldを使うとキーボードが出た時、下記のように特に問題はないです。
しかし、普通デザインのためColumnウィジェットとTextFieldを使います。
この時、Columnのエリアの上にキーボードが表示されると下記のようなワーニングがでます。
このワーニングを解決するため、使えるものがSingleChildScrollViewウィジェットです。
SingleChildScrollViewウィジェットを次のように使うと上の問題を解決することができます。
SingleChildScrollView( child: Column( children: [ Container( width: 300, height: 300, margin: EdgeInsets.all(40.0), color: Colors.lightBlue, ), Padding( padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0), child: TextField( decoration: InputDecoration( labelText: 'Email', hintText: 'Enter your email', labelStyle: TextStyle(color: Colors.redAccent), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), ), ), keyboardType: TextInputType.emailAddress, ), ), ], ), )SingleChildScrollViewを使うと、TextFieldでキーボードがアクティブされた時、画面がスクロールできる状態になって発生した問題が解決されます。
GestureDetectorとFocusScope
現在キーボードがアクティブになると、キーボードのdoneボタンを押さないとキーボードは消えません。つまり、TextFieldがFocusの状態になると、キーボードがアクティブになり、doneキーを押してTextFieldがUnFocusの状態になるとキーボードが消えます。
普通のアプリのUXはキーボードがアクティブになると、キーボード以外のエリアをタッチすると、キーボードが消えます。このようにキーボード以外のエリアをタッチした時、キーボードが消えるようにするためGestureDetectorウィジェットとFocusScopeウィジェットを使います。
それじゃ、キーボード以外のエリアをタッチした時、キーボードを消すため、main.dartファイルを次のように修正します。
GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: SingleChildScrollView(...), ),SingleChildScrollViewウィジェット中は上で説明したコードなので省略しました。まず、ユーザのイベントを検知するためGestureDetectorを使いました。この時、ユーザが画面をタッチした場合、キーボードからFocusを消すため、FocusScopeウィジェットのunfocus関数を使いました。
このようにGestureDetectorとFocusScopeを使うと、キーボードを消す機能を使えます。
TextFieldの値を使う方法
TextFieldを使う理由はユーザから値を入力して貰って、入力した貰った値を使うためです。それじゃ、TextFieldの値を使う方法について説明します。
onChangedユーザがTextFieldに値を入れるとTextFieldウィジェットのonChanged関数がコールされます。この関数がコールされる時、パラメーターで渡されるtextの値をsetStateを使って保存すれば良いです。
class Home extends StatefulWidget { @override _HomeState createState() => _HomeState(); } class _HomeState extends State<Home> { String inputText = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('TextField'), ), body: Center( child: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: SingleChildScrollView( child: Column( children: [ Text('$inputText'), Padding( padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0), child: TextField( onChanged: (text) { setState(() { inputText = text; }); }, decoration: InputDecoration( labelText: 'Email', hintText: 'Enter your email', labelStyle: TextStyle(color: Colors.redAccent), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), ), ), keyboardType: TextInputType.emailAddress, ), ), ], ), ), ), ), ); } }値が変更される部分だけもっと詳しく見ると。
... String inputText = ''; ... Text('$inputText') ... TextField( onChanged: (text) { setState(() { inputText = text; }); }, ..., ) ...変更された値を保存するため、StatefulWidgetを生成しました。そして、ユーザの入力を保存するためString変数を定義しました。このように生成したString変数をTextウィジェットを使って画面へ表示しました。
そしてTextFieldウィジェットのonChanged関数を使ってユーザが入力した値をsetStateを使って宣言した変数を変更しました。
今から、TextFieldの値を入力すると、次のようにTextFieldの上に入力した内容が出力されることが確認できます。
TextEditingController上のようにリアルタイムでデータを更新することもできますが、特定なイベントが発生した時、現在入力された値にアクセスしたい時もあります。この時、使うものたTextEditingControllerです。
TextEditingControllerは次のように使えます。
class _HomeState extends State<Home> { TextEditingController inputController = TextEditingController(); String inputText = ''; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('TextField'), ), body: Center( child: GestureDetector( onTap: () => FocusScope.of(context).unfocus(), child: SingleChildScrollView( child: Column( children: [ Text('$inputText'), Padding( padding: EdgeInsets.fromLTRB(20.0, 0.0, 20.0, 10.0), child: TextField( controller: inputController, decoration: InputDecoration( labelText: 'Email', hintText: 'Enter your email', labelStyle: TextStyle(color: Colors.redAccent), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), borderSide: BorderSide(width: 1, color: Colors.redAccent), ), border: OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(10.0)), ), ), keyboardType: TextInputType.emailAddress, ), ), ElevatedButton( onPressed: () { setState(() { inputText = inputController.text; }); }, child: Text('Update'), ), ], ), ), ), ), ); } }そしたら、TextEditingControllerを使って入力した値を画面に表示する部分だけみてみましょう。
TextEditingController inputController = TextEditingController(); String inputText = ''; ... Text('$inputText'), ... TextField( controller: inputController, ..., ), ... ElevatedButton( onPressed: () { setState(() { inputText = inputController.text; }); }, child: Text('Update'), ), ...まずTextEditingControllerを宣言した後、TextFieldウィジェットのcontrollerパラメーターに渡します。そして、ElevatedButtonボタンが押せた時、setStateを使って変数をアップデートします。この時、inputController.textのようにTextFieldウィジェットの入力値にアクセスすることができます。
この方法は主にサーバへデータを送るとき使います。
ScaffoldのresizeToAvoidBottomInset
TextFieldウィジェットを使って開発する時、次のようにキーボードとコンテンツの間に空間が表示される場合が発生します。
この時はScaffoldのresizeToAvoidBottomInsetオプションを使うと問題を解決することができます。
Scaffold( resizeToAvoidBottomInset: false, appBar: ..., body: GestureDetector( onTap: () => Get.focusScope!.unfocus(), child: SingleChildScrollView( child: Column( children: [ ..., ], ), ), ), );このようにScaffoldのresizeToAvoidBottomInsetオプションをfalseで設定すると次のように問題が解決されることが確認できます。
完了
これでTextFieldウィジェットを使ってユーザが入力した値にアクセスして使う方法についてみてみました。
Github Sponsor Buy me a coffee私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!