[Flutter]회원가입 화면 구현(Statusbar, InputField)

CHO WanGi·2024년 8월 14일

Flutter

목록 보기
23/27
post-thumbnail

현재 진행중인 프로젝트에서 만들었던 회원가입 위젯을 만드는 과정을 공유하고자 합니다.

Demo

  1. InputField 에 값을 입력하면 상단 bar가 채워지면서 사용자가 진행률을 직관적으로 확인할 수 있도록 의도하였습니다.

  2. 사용자가 입력 필드를 채우고,Validator를 실행하여, 유효하지 않으면 다음 입력란이 등장하지 않게 하여 BE로 잘못된 값이 넘어가지 않도록 설정하였습니다. 또한 유효성 검사를 통과하면 다음 입력 필드가 등장하도록 하였습니다.

  3. Firebase를 활용하여 전화번호 인증 기능을 추가하였습니다.

  4. 회원가입 성공이후 메인 페이지로 넘어가며 입력된 정보를 POST 합니다.

  5. env 파일을 활용하여 API_KEY가 깃헙에 올라가지 않도록 하였습니다.
    (3,4,5번 항목은 추후에 정리할 예정입니다.)

1. 상단 StatusBar 구현

Imported Package

Code

  int _visibleFieldCount = 1;
  double _progressPercent = 0.0;
  void _updateVisibleFields(int newCount) {
    setState(() {
      _visibleFieldCount = newCount;
      _progressPercent = _visibleFieldCount / 9;
    });
  }
  
 LinearPercentIndicator(
          // 💡 이 부분에 주석을 해제하면 Statusbar 의 수치를 확인하실 수 있습니다.
          // center: Text(
          //   "$_progressPercent",
          //   style: const TextStyle(fontSize: 16),
          // ),
          barRadius: const Radius.circular(8.0),
          // IOS, Android 둘다 overflow 안나는 값으로 설정해두었습니다
          width: 320,
          lineHeight: 16.0,
          percent: _progressPercent <= 0.9
              ? _progressPercent
              : 1.0, // 1.0 이상 올라가지 않게 설정
          progressColor: _progressPercent >= 0.9
              ? ColorStyles.secondaryOrange
              : ColorStyles.orange,
        )
        // build
        Column(
              children: [
                SignUpForm(
                  onProgressChanged: _updateVisibleFields,
                ),
        
// sign_up_form.dart
                            widget.onProgressChanged(_visibleFields.length);
  1. Package의 Example과 Readme를 참고하여 거의 수정없이 구현하였습니다
  2. percent 값에 _progressPercent를 삼항연산를 통하여 0.9 이상이 되면 일괄적으로 1.0으로 바꾸어놨습니다.

TroubleShooting

2번을 굳이굳이 해놓은 이유는 _progressPercent 값이 1.0을 넘어가면 에러를 뱉어냈기 때문!

  1. 하위 클래스인 SignUpForm 에서 _visibleFields 의 길이를 callback으로 상위 위젯으로 넘겨주었습니다.

  2. 상위 위젯인 GetUserInfoScrceen 에서 이 길이를 9로 나누어 _progressPercent를 계산합니다.

  3. 계산된 값에 따라서 Statusbar가 채워지고, 마지막 인증번호를 채우면 0.9가 초과되어, 삼항연산자에 따라 1.0으로 바뀌고, 색깔도 더 진한 주황색으로 변경됩니다.
    (데모에서 안보이는 이유는 실제 전화번호를 적다보니... 밑에 사진처럼 바뀝니다!)

2. 입력필드 하나씩 등장 & 유효성검사

유효성검사

유효성 검사란 사용자가 입력필드에 입력한 값이 개발자인 우리가 의도한 값과 일치하는지를 검사하는 것 입니다.

이렇게 이메일 형식을 지켰는지

비밀번호 규칙에 맞게 사용자가 설정했는지

비밀번호 확인란에 동일한 비밀번호를 입력했는지

등등을 확인하는 것을 유효성 검사라고 합니다.

이렇게 따로 Validator라는 클래스를 만들어 놓고
유효성검사가 필요한 곳에 가져와서 활용하니까, 회원가입 로직의 코드도 줄고,
재사용이나 유지보수가 훨씬 더 편한것 같습니다 :)

(사실 다른 플젝에서 썼던거 가져와서 조금 수정해서 그대로 썼습니다...^^)

유효성 검사 Code


class Validator {
  static bool isPasswordCompliant(String password,
      [int minLength = 8, int maxLength = 20]) {
    if (password.isEmpty) {
      return false;
    }
    bool hasProperLength =
        password.length >= minLength && password.length <= maxLength;
    bool hasNumber = password.contains(RegExp(r'\d'));
    bool hasLetter = password.contains(RegExp(r'[a-zA-Z]'));
    bool hasSpecialCase = password.contains(RegExp(r'[!@#\$%^&]'));

    return hasProperLength && hasNumber && hasLetter && hasSpecialCase;
  }

  static String? validateName(String? value) {
    if (value == null || value.trim().isEmpty) {
      return '빈칸을 채워주세요';
    }
    return null;
  }

  static String? validateEmail(String? value) {
    if (value == null || value.trim().isEmpty) {
      return '이메일 형식을 지켜주세요';
    } else if (!RegExp(r'\S+@\S+\.\S+').hasMatch(value)) {
      return '이메일 형식을 지켜주세요';
    }
    return null;
  }

  static String? validatePassword(String? value) {
    if (!isPasswordCompliant(value!)) {
      return '문자+숫자+특수문자 조합 8자 이상 20자 미만으로 구성해주세요';
    }
    return null;
  }

  static String? confirmPassword(String? value, String? password) {
    if (value != password) {
      return '동일한 비밀번호를 입력해주세요';
    }
    return null;
  }
}
// 유효성검사를 가져와서 활용하는 부분

  TextEditingController? controller = _controllers[label.toLowerCase()];
    if (controller == null) {
      controller = TextEditingController();
      _controllers[label.toLowerCase()] = controller;
    }

    String? Function(String?)? validator;
    switch (label.toLowerCase()) {
      case 'e-mail(id)':
        validator = Validator.validateEmail;
        break;
      case 'password':
        validator = Validator.validatePassword;
        break;
      case 'confirm password':
        validator = (value) =>
            Validator.confirmPassword(value, _controllers['password']!.text);
        break;
      case 'first name':
      case 'last name':
        validator = Validator.validateName;
        break;
    }

입력필드가 차례로 등장하는 기능

일단, 먼저 제가 잘못한 부분이 controller의 이름(label)을 어디에는 대문자로 해놓고, 어디에는 소문자로 해놔서 label이 일치하지 않아서 원하는 기능을구현하는데 애를 많이 먹었습니다.

그래서 이렇게 toLowerCase로 다 바꿔서 사용합니다^^

TextEditingController? controller = _controllers[label.toLowerCase()];

Logic

코드가 너무 길어서 전부를 공유하기는 어렵고 로직을 설명드리겠습니다.

  1. _visibleFields 리스트 생성
  final List<String> _visibleFields = ['E-mail(ID)'];

리스트에 들어가는 문자열은 controller의 label 입니다.

  1. 사용자의 입력과 유효성 검사를 통과하면 리스트에 controller의 label을 추가

Switch 문법을 활용하였고, 초기값인 이메일 입력필드를 채우고 유효성검사를 통과하면 리스트에 Password 문자열을 추가하였습니다.

if (value.isNotEmpty) {
	switch (label.toLowerCase()) {
		case 'e-mail(id)':
		if (!_visibleFields.contains('Password')) {
			if (_formKey.currentState!.validate()) {
				setState(() => _visibleFields.add('Password'));
                	}
                }
		break;
}
  1. onchange 옵션에 달아서 리스트의 변화를 확인하면서 해당하는 label의 입력필드를 보여주게 됩니다.

결론

Velog에 정리하면서 코드를 살펴보니
입력필드를 차례차례 보여주는 기능을 리팩토링해야 할 것만 같습니다...
이제 곧 다른 분과 협업을 할텐데
제가 봐도 코드가 복잡한데 그 분은... 많이 힘들거 같네요

리스트에 문자열을 추가하고 문자열과 동일한 label을 가진 Controller를 사용하는 TextFormField를 보여준다

라는 설명이 제가 봐도 참 어렵네요...ㅠ

3,4,5번보다 리팩토링 결과에 대해 Velog에 먼저 써야할 것 같습니다ㅠ

profile
제 Velog에 오신 모든 분들이 작더라도 인사이트를 얻어가셨으면 좋겠습니다 :)

0개의 댓글