티스토리 뷰

Flutter

Form 필드로 유효성 체크하기

순진이 2024. 7. 2. 17:20

Form Widget

  • 유저의 입력을 받을 때, 그 유효성을 체크하도록 도와주는 위젯
  • TextFormField, DropdownButtonFormField, CheckboxFormField 등 다양한 입력 필드 위젯들을 그룹화하여 담아주는 컨테이너

 

일단 사용법부터 알아보자.

 


사용법

  1. GlobalKey<FormState> 타입의 키 만들기
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

 


 

2. Form 위젯과 그 child로 TextFormField, DropdownButtonFormField, CheckboxFormField 등 유효성 체크가 필요한 위젯들 넣기

※ key 속성에 1번에 선언한 글로벌 키 넣기

child: Form(
  key: _formKey,
  child: Column(
    mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        TextFormField(),
        const SizedBox(height: 30),
        TextFormField(),
        const SizedBox(height: 30),
        TextFormField(),
        const SizedBox(height: 30),
        ElevatedButton(onPressed: submit, child: Text('제출하기'))
      ],
    ),
  )
)

 


 

3. 각 TextFromField의 유효성 체크 해주기 

  • String? Function(T? value) validator : 유효성을 체크하는 곳
    • 유효하지 않은 경우 ‘이름을 입력하세요’ 등과 같은 에러 메세지를 리턴할 수 있음
    • 그 외에는 null;을 리턴함
  • void Function(T? newValue) onSaved
    • _formKey.currentState!.save();가 호출되면 불리는 콜백함수
TextFormField(
  validator: (value) {
    debugPrint('$value');
    if (value == null || value.isEmpty) {
      return '이름을 입력하세요';
    }
    return null;
  },
  onSaved: (newValue) {
    if (newValue == null) return;
    name = newValue;
  },
    decoration: InputDecoration(labelText: '이름'),
),

 

 

 

 

아래처럼 아무것도 입력하지 않고 제출하기 버튼을 누르면 validator에서 설정한 에러 메시지가 나타남


 

 

 

4. 제출하기 버튼

  • 우선 모든 _formkey 아래 입력 필드들의 유효성을 체크해줄 버튼
    • GlobalKey<FormState> 타입의 _formKey를 만들면서 자동으로 currentState()가 생성됨
    • _formKey.CurrentState()!.validate() -> Form 아래에 속한 모든 입력 필드의 유효성 검사를 해서 문제가 없으면 true, 문제가 있으면 false를 리턴함 (나 같은 경우는 3개의 TextFormField가 모두 true여야 true return)
    • 모든 유효성 체크가 true라면 save() 메서드를 호출 → Form 아래 모든 입력필드 들의 onSaved()가 호출됨
ElevatedButton(onPressed: submit, child: Text('제출하기'))

  void submit() {
    //1.
    if (_formKey.currentState != null) {
      if (_formKey.currentState!.validate()) {
        _formKey.currentState!.save();
      }
    }

    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => ResultScreen(),
        settings: RouteSettings(
            arguments: {"name": name, "age": age, "breed": breed}),
      ),
    );
  }

 

이제 onSaved() 콜백 호출에서 String? newValue로 입력된 스트링 값이 들어오니 이 값을 활용하면 됨!

 


추가적으로 Form뿐만 아니라 그 하위 요소로 쓰였던 TextFormField, DropdownButtonFormField, CheckboxFormField 들은 AutovalidateMode 타입의 autovalidateMode라는 속성이 있음

 

 

말그대로 validate를 자동으로 해주는 속성인 것 같음. 생김새는 아래와 같음

 

이놈은 AutovalidateMode라는 enum형태의 타입이고, disabled, always, onUserInteraction 케이스가 있음. default는 disabled임

이걸 name을 적는 TextFormField에 onUserInteraction으로 적용해보겠음! 이렇게 하면 유저의 인터랙션이 있어야 자동 유효성 검사가 시작됨!

 

TextFormField(
  autovalidateMode: AutovalidateMode.onUserInteraction,

  validator: (value) {
    if (value == null || value.isEmpty) {
      return '이름을 입력하세요';
    }
    return null;
  },
  
  onSaved: (newValue) {
    if (newValue == null) return;
    name = newValue;
  },
  decoration: InputDecoration(labelText: '이름'),
),

 

 

 

이렇게 했더니 name TextFormField는 입력을 할 때마다 유효성 검사가 실행돼서 아무것도 안 쓴 경우 내가 설정한 에러 메시지가 나오는 걸 볼 수 있음!

 

 

이 속성은 Form 위젯 자체에도 있으니 모든 폼필드에 적용하고 싶을 때는 그렇게 하면 되겠음!

 

 


그래서 제출하기 버튼을 누르면 아래처럼 진행된다고 할 수 있겠음

 

 

전체코드

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Form Widget',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'Form Widget'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  //GlobalKey<FormState>의 키 만들기
  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();

  AutovalidateMode autovalidateMode = AutovalidateMode.onUserInteraction;

  //TextFormField에 입력될 값들
  String name = '';
  String age = '';
  String breed = '';

  void submit() {
    if (_formKey.currentState != null) {
      if (_formKey.currentState!.validate()) {
        _formKey.currentState!.save();
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
      ),
      body: Form(
        key: _formKey,
        child: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              TextFormField(
                autovalidateMode: AutovalidateMode.onUserInteraction,
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '이름을 입력하세요';
                  }
                  return null;
                },
                onSaved: (newValue) {
                  if (newValue == null) return;
                  name = newValue;
                },
                decoration: InputDecoration(labelText: '이름'),
              ),
              const SizedBox(height: 30),
              TextFormField(
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '나이를 입력하세요';
                  }
                  return null;
                },
                onSaved: (newValue) {
                  if (newValue == null) return;
                  age = newValue;
                },
                decoration: InputDecoration(labelText: '나이'),
              ),
              const SizedBox(height: 30),
              TextFormField(
                validator: (value) {
                  if (value == null || value.isEmpty) {
                    return '견종을 입력하세요';
                  }
                  return null;
                },
                onSaved: (newValue) {
                  if (newValue == null) return;
                  breed = newValue;
                },
                decoration: InputDecoration(labelText: '견종'),
              ),
              const SizedBox(height: 30),
              ElevatedButton(onPressed: submit, child: Text('제출하기'))
            ],
          ),
        ),
      ),
    );
  }
}

 

지금까지는 TextField만 써봤었는데, 오늘 처음으로 Form을 연습해봤음!

TextField에서는 별도의 TextEditingController를 설정해서 관리하느라 유저의 정보를 입력받는 곳에서는 너무 많은 많은 컨트롤러가 만들어지고 그 컨트롤러마다 관리하기도 어려웠는데, 이렇게 하니 꽤 간편해져서 좋았다!

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크