Frontend/Flutter

[Flutter] TextFormField를 사용하여 textfield 상태관리 하기

김룹 2024. 2. 21. 10:14

 

 

유효성을 검사할 수 있는 옵션이 존재하는가?
=> Form 기능 + TextField 기능 = TextFormField

 

 

 

Form위젯 사용하여 textField 상태관리 하는 방법

플러터에서 TextField를 입력 받으려면 기본적으로 TextEditingController를 사용해야 한다.

TextField가 하나라면 괜찮지만 많아질수록 컨트롤러 관리가 어려워진다 @_@

하지만 TextFormField를 사용하면 쉽게 validation과 값을 받아올 수 있음 ! 

 

 

 

기본 레이아웃

import 'package:flutter/material.dart';

class FormScreen extends StatefulWidget {
  @override
  _FormScreenState createState() => _FormScreenState();
}

class _FormScreenState extends State<FormScreen> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [ 
          // 여기에 폼 작성하기
        ],
      ),
    );
  }
}

 

 

 

Form 위젯 사용하기

  final formKey = GlobalKey<FormState>();

  @override
  Widget build(BuildContext context) {
    return DefaultAppbarLayout(
      child: Form(
        key: this.formKey,
        child: Column(
          children: [ 
            // 여기에 TextFormField들 입력하기!
          ],
        ),
      ),
    );
  }

✔️ Form 위젯은 childkey 파라미터를 받는다.

  • child에는 TextFormField들을 넣어준다
  • key에는 GlobalKey를 넣어주면 됨! 이 key는 나중에 폼 내부의 TextFormField 값들을 저장하고 validation을 진행하는데 사용된다.

 

 

 

TextFormField 위젯 생성 함수

  renderTextFormField({
    @required String label,
    @required FormFieldSetter onSaved,
    @required FormFieldValidator validator,
  }) {
    assert(onSaved != null);
    assert(validator != null);

    return Column(
      children: [
        Row(
          children: [
            Text(
              label,
              style: TextStyle(
                fontSize: 12.0,
                fontWeight: FontWeight.w700,
              ),
            ),
          ],
        ),
        TextFormField(
          onSaved: onSaved,
          validator: validator,
        ),
      ],
    );
  }

 

 

 

 

TextFormField의 onSaved, validator 파라미터

 @override
  Widget build(BuildContext context) {
    return DefaultAppbarLayout(
      child: Form(
        key: this.formKey,
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            children: [
              renderTextFormField(
                label: '이름',
                onSaved: (val) {},
                validator: (val) {
                  return null;
                },
              ),
              renderTextFormField(
                label: '이메일',
                onSaved: (val) {},
                validator: (val) {
                  return null;
                },
              ),
              renderTextFormField(
                label: '비밀번호',
                onSaved: (val) {},
                validator: (val) {
                  return null;
                },
              ),
              renderTextFormField(
                label: '주소',
                onSaved: (val) {},
                validator: (val) {
                  return null;
                },
              ),
              renderTextFormField(
                label: '닉네임',
                onSaved: (val) {},
                validator: (val) {
                  return null;
                },
              ),
            ],
          ),
        ),
      ),
    );
  }

 

 

✔️ TextFormFieldonSavedvalidator 파라미터를 받는다.

  • onSaved의 시그니처는 FormFieldSetter 라는 typedef
  • validator의 시그니처는 FormFieldValidator
  • 둘 다 String을 받고 있고, validator는 String의 리턴값 또한 받는다. 리턴되는 String은 에러메시지로 사용된다

 

 

 

폼 저장 버튼 생성

 renderButton() {
    return RaisedButton(
      color: Colors.blue,
      onPressed: () {},
      child: Text(
        '저장하기!',
        style: TextStyle(
          color: Colors.white,
        ),
      ),
    );
  }

 

 

 

폼 저장 후 Snackbar 보여주기

  renderButton() {
    return RaisedButton(
      color: Colors.blue,
      onPressed: () async {
        if(this.formKey.currentState.validate()){
          // validation 이 성공하면 true 리턴!
          
          // validation 이 성공하면 폼 저장하기
          this.formKey.currentState.save();
          
          Get.snackbar(
            '저장완료!',
            '폼 저장이 완료되었습니다!',
            backgroundColor: Colors.white,
          );
        }

      },
      child: Text(
        '저장하기!',
        style: TextStyle(
          color: Colors.white,
        ),
      ),
    );
  }

✔️ formKey.currentState.validate() 함수를 실행하면 Form 내부에 있는 TextFormField들의 validation 결과에 따라 성공이면 true 리턴, 실패하면 false 리턴해준다.

 

 

 

Validator 파라미터 작성

⚠️ TextField에 아무것도 입력하지 않아도 저장이 된다. 모든 TextField의 validation 파라미터를 return null로 저장해서 그렇다. TextField 별로 적절한 에러 메시지 작성하기!

  @override
  Widget build(BuildContext context) {
    return DefaultAppbarLayout(
      child: Form(
        key: this.formKey,
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            children: [
              renderTextFormField(
                label: '이름',
                onSaved: (val) {},
                validator: (val) {
                  if(val.length < 1) {
                    return '이름은 필수사항입니다.';
                  }

                  if(val.length < 2) {
                    return '이름은 두글자 이상 입력 해주셔야합니다.';
                  }

                  return null;
                },
              ),
              renderTextFormField(
                label: '이메일',
                onSaved: (val) {
                },
                validator: (val) {
                  if(val.length < 1) {
                    return '이메일은 필수사항입니다.';
                  }

                  if(!RegExp(
                      r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
                      .hasMatch(val)){
                    return '잘못된 이메일 형식입니다.';
                  }

                  return null;
                },
              ),
              renderTextFormField(
                label: '비밀번호',
                onSaved: (val) {},
                validator: (val) {
                  if(val.length < 1) {
                    return '비밀번호는 필수사항입니다.';
                  }

                  if(val.length < 8){
                    return '8자 이상 입력해주세요!';
                  }
                  return null;
                },
              ),
              renderTextFormField(
                label: '주소',
                onSaved: (val) {},
                validator: (val) {
                  if(val.length < 1) {
                    return '주소는 필수사항입니다.';
                  }
                  return null;
                },
              ),
              renderTextFormField(
                label: '닉네임',
                onSaved: (val) {},
                validator: (val) {
                  if(val.length < 1) {
                    return '닉네임은 필수사항입니다.';
                  }
                  if(val.length < 8) {
                    return '닉네임은 8자 이상 입력해주세요!';
                  }
                  return null;
                },
              ),
              renderButton(),
            ],
          ),
        ),
      ),
    );
  }

 

 

 

 

onSaved 파라미터 작성하기

  final formKey = GlobalKey<FormState>();

  String name = '';
  String email = '';
  String password = '';
  String address = '';
  String nickname = '';

  @override
  Widget build(BuildContext context) {
    return DefaultAppbarLayout(
      child: Form(
        key: this.formKey,
        child: Padding(
          padding: EdgeInsets.all(16.0),
          child: Column(
            children: [
              renderTextFormField(
                label: '이름',
                onSaved: (val) {
                  setState(() {
                    this.name = val;
                  });
                },
                validator: (val) {
                  if(val.length < 1) {
                    return '이름은 필수사항입니다.';
                  }

                  if(val.length < 2) {
                    return '이름은 두글자 이상 입력 해주셔야합니다.';
                  }

                  return null;
                },
              ),
              renderTextFormField(
                label: '이메일',
                onSaved: (val) {
                  setState(() {
                    this.email = val;
                  });
                },
                validator: (val) {
                  if(val.length < 1) {
                    return '이메일은 필수사항입니다.';
                  }

                  if(!RegExp(
                      r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$')
                      .hasMatch(val)){
                    return '잘못된 이메일 형식입니다.';
                  }

                  return null;
                },
              ),
              renderTextFormField(
                label: '비밀번호',
                onSaved: (val) {
                  setState(() {
                    this.password = val;
                  });
                },
                validator: (val) {
                  if(val.length < 1) {
                    return '비밀번호는 필수사항입니다.';
                  }

                  if(val.length < 8){
                    return '8자 이상 입력해주세요!';
                  }
                  return null;
                },
              ),
              renderTextFormField(
                label: '주소',
                onSaved: (val) {
                  setState(() {
                    this.address = val;
                  });
                },
                validator: (val) {
                  if(val.length < 1) {
                    return '주소는 필수사항입니다.';
                  }
                  return null;
                },
              ),
              renderTextFormField(
                label: '닉네임',
                onSaved: (val) {
                  setState(() {
                    this.nickname = val;
                  });
                },
                validator: (val) {
                  if(val.length < 1) {
                    return '닉네임은 필수사항입니다.';
                  }
                  if(val.length < 8) {
                    return '닉네임은 8자 이상 입력해주세요!';
                  }
                  return null;
                },
              ),
              renderButton(),
            ],
          ),
        ),
      ),
    );
  }

✔️ 값을 받아보기 위해 onSaved 파라미터에서 저장 시 위젯의 변수에 값을 저장하는 기능 작성! 

 

 

 

 

💡 꿀팁 ! 

TextFormFieldautovalidateModeAutovalidateMode.always로 지정하면, 저장하기 버튼을 누르기 전에 각 TextFormField가 자동으로 validation을 진행하는 것을 볼 수 있다! 

 

 

 

 

 

 

🌐 참고 링크

https://youtu.be/V4xk4pmzY2I