앱이든 웹이든 입력필드를 만들다보면 validation을 통해 입력값을 프론트엔드에서 1차적으로 필터링해주는 작업을 하게된다.
플러터는 이러한 validation작업을 위해 TextFormField 위젯에 validator
프로퍼티를 통해 손쉽게 validation을 수행할 수 있도록 해준다.
이번 글에서는 이를 구현하는 방식을 소개하려고한다.
우선 첫 번째로, 아래의 코드처럼 GlobalKey<FormState>
변수를 선언 한뒤 Form위젯에 key
로 지정한다.
이 때, Form위젯의 child
는 validation을 수행할 입력 필드를 TextFormField를 이용해 넣어준다.
GlobalKey<FormState> formKey = GlobalKey<FormState>();
Form(
key: formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Name'),
),
TextFormField(
decoration: const InputDecoration(labelText: 'Age'),
),
TextFormField(
decoration: const InputDecoration(labelText: 'City'),
),
],
),
),
그 다음 각각의 TextFormField가 가진 validator
에 수행할 validation 코드를 작성한다.
Form(
key: formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Name'),
validator: (String? value) {
if (value?.isEmpty ?? true) return 'Empty!';
if (value!.contains(RegExp(r'[0-9]'))) return 'Number is not allowed!';
return null;
},
),
TextFormField(
decoration: const InputDecoration(labelText: 'Age'),
validator: (String? value) {
if (value?.isEmpty ?? true) return 'Empty!';
if (value!.contains(RegExp(r'[^0-9]'))) return 'Not Number!';
return null;
},
),
TextFormField(
decoration: const InputDecoration(labelText: 'City'),
validator: (String? value) {
if (value?.isEmpty ?? true) return 'Empty!';
return null;
},
),
],
),
),
마지막으로 validation의 트리거가 될 액션을 넣어준다. 이때 formKey.currentState!.validate()
를 통해 위젯트리에서 Form위젯 하위에 존재하는 모든 validator를 검증한다.
Text(validationResult ? 'Success' : 'Failed'),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: () => setState(
() {
//validation의 결과를 true/false로 반환한다.
validationResult = formKey.currentState?.validate() ?? false;
},
),
child: const Text('Validation'),
),
전체 코드는 다음과 같다.
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
Widget build(BuildContext context) {
return const MaterialApp(
home: Home(),
);
}
}
class Home extends StatefulWidget {
const Home({super.key});
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
GlobalKey<FormState> formKey = GlobalKey<FormState>();
late bool validationResult;
void initState() {
validationResult = false;
super.initState();
}
Widget build(BuildContext context) {
return GestureDetector(
onTap: () => FocusScope.of(context).unfocus(),
child: Scaffold(
appBar: AppBar(title: const Text('Test')),
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
children: [
Form(
key: formKey,
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
labelText: 'Name',
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Empty!';
if (value!.contains(RegExp(r'[0-9]'))) return 'Number is not allowed!';
return null;
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Age',
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Empty!';
if (value!.contains(RegExp(r'[^0-9]'))) return 'Not Number!';
return null;
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'City',
),
validator: (value) {
if (value?.isEmpty ?? true) return 'Empty!';
return null;
},
),
],
),
),
const SizedBox(height: 24.0),
Text(validationResult ? 'Success' : 'Failed'),
const SizedBox(height: 24.0),
ElevatedButton(
onPressed: () => setState(
() {
validationResult = formKey.currentState?.validate() ?? false;
},
),
child: const Text('Validation')),
],
),
),
),
),
);
}
}