対象者
- Flutterを学び始めた初心者
- フォームデザインに興味がある方
- 簡単なフォームの作り方を知りたい方
- お洒落なUIを作りたい開発者
はじめに
この記事では、Flutterを使ってお洒落なTextFormFieldを作成する方法を説明します。
簡単なフォーム検証を行うアプリの一部で、入力が必須のテキストフィールドを含みます。入力がある場合は「検証成功!」と表示し、そうでない場合は「検証失敗!」と表示します。
実施するソース
TextFormField
以下のように設定して、お洒落な(? 及第点にはいっていると思う)TextFormFieldが作成します。入力の検証とともに、見た目にもこだわったデザインが実現します。
最初にベースとなる通常状態のデザインのborderStyleを定義して、フォーカスが当たったとき(枠線が太枠になる)と検証失敗したとき(枠線が赤くなる)を通常状態の差分で定義します。
final borderStyle = OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: const BorderSide(
width: 1,
),
);
TextFormField(
validator: (value) {
if (value?.isEmpty ?? true) {
return '必須です';
}
return null;
},
decoration: InputDecoration(
border: borderStyle,
focusedBorder: borderStyle.copyWith(
borderSide: const BorderSide(width: 2),
),
enabledBorder: borderStyle,
errorBorder: borderStyle.copyWith(
borderSide: const BorderSide(
color: Colors.red,
),
),
disabledBorder: borderStyle.copyWith(
borderSide: const BorderSide(
color: Colors.grey,
),
),
labelStyle: Theme.of(context).textTheme.bodyMedium,
labelText: '電話番号',
hintText: '09012345678',
),
-
borderStyle(ベース) を定義
OutlineInputBorder ウィジェットを使用して、TextFormFieldのボーダースタイルを設定します。ここでは、角の丸みを16に設定し、ボーダーの幅を1にしています。このスタイルは後でTextFormFieldの複数のボーダープロパティで使用されます。 -
validator を設定
TextFormFieldのvalidatorプロパティに関数を設定します。この関数は、フォームが検証されるときに呼び出され、入力値をチェックします。入力が空の場合、「必須です」というエラーメッセージが表示されます。 -
decoration を設定
TextFormFieldの見た目をカスタマイズするために、InputDecoration ウィジェットを使用します。
以下のプロパティを設定しています。
- border
通常状態のボーダースタイルです。 borderStyle をそのまま設定します。 - focusedBorder
フォーカス時のボーダースタイルです。borderStyle をコピーし、ボーダー幅を2に変更して太線にしたものを設定します。 - enabledBorder
有効状態のボーダースタイルです。borderStyle を設定します。 - errorBorder
エラー状態のボーダースタイルです。borderStyle をコピーし、ボーダーの色を赤に変更したものを設定します。これにより、検証エラーが発生した場合にボーダーが赤くなります。 - labelStyle
ラベルのテキストスタイルに、現在のテーマのbodyMedium スタイルを設定します。 - labelText
ラベルのテキストに、「電話番号」と表示します。 - hintText
テキストフィールドにヒントテキスト「09012345678」を表示します。これは入力前に薄く表示され、ユーザーがどのような入力が期待されるかを理解しやすくします。
Form部分
フォーム検証を行う部分です。_formKeyを使用して、フォームの状態を管理し、FilledButtonを押すことでフォームの検証を行います。
検証が成功した場合は、ボタンのラベルが「検証成功!」になり、失敗した場合は「検証失敗!」になります。
final _formKey = GlobalKey<FormState>();
var _label = '検証';
@override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
FilledButton(
onPressed: () {
setState(() {
_label =
_formKey.currentState!.validate() ? '検証成功!' : '検証失敗!';
});
},
child: Text(_label)),
],
),
),
);
}
フォームに入力された値の検証は、_formKey.currentState!.validate()で行われています。
FormがStatefulWidgetのため、_formKey.currentState!にて対応するState(この場合、Formに対応するFormState)にアクセスすることができます。そして、FormStateに定義されているValidateを呼び出してます。
まとめ
このブログでは、Flutterを使ってお洒落なTextFormFieldを作成する方法を紹介しました。TextFormField ウィジェットとそのデコレーションプロパティを使用することで、見た目にこだわったテキストフィールドを簡単に実装することができます。
また、Formを使って検証を行う方法も簡単に解説しました。
ぜひ、この知識を活用して、素敵なUIを作成してみてください。
全ソース
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(
primarySwatch: Colors.blue,
),
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 createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
final borderStyle = OutlineInputBorder(
borderRadius: BorderRadius.circular(16),
borderSide: const BorderSide(
width: 1,
),
);
final _formKey = GlobalKey();
var _label = '検証';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextFormField(
validator: (value) {
if (value?.isEmpty ?? true) {
return '必須です';
}
return null;
},
decoration: InputDecoration(
border: borderStyle,
focusedBorder: borderStyle.copyWith(
borderSide: const BorderSide(width: 2),
),
enabledBorder: borderStyle,
errorBorder: borderStyle.copyWith(
borderSide: const BorderSide(
color: Colors.red,
),
),
disabledBorder: borderStyle.copyWith(
borderSide: const BorderSide(
color: Colors.grey,
),
),
labelStyle: Theme.of(context).textTheme.bodyMedium,
labelText: '電話番号',
hintText: '09012345678',
),
),
FilledButton(
onPressed: () {
setState(() {
_label =
_formKey.currentState!.validate() ? '検証成功!' : '検証失敗!';
});
},
child: Text(_label)),
],
),
),
);
}
}