[Flutter] 로그인, 유효성검사

merci·2023년 4월 1일
0

Flutter

목록 보기
6/24
post-custom-banner

routes 프로퍼티를 이용하면 일반적인 <a link>처럼 매핑을 할 수 있다.

	MaterialApp(
      initialRoute: "/login",
      routes: 
        "/login": (context) => LoginPage(),  
        "/home": (context) => HomePage()
      },);

위처럼 라우팅을 하면 home: LoginPage(),같은 고정된 페이지를 매핑시키지 않고 동적으로 매핑시킬수 있다.
코드 테스트할때 initialRoute: "/home",로 바꾼다면 부분 수정이 아니므로 핫리로드 대신 에뮬레이터를 다시 실행해야 한다.

로그인페이지 만들기

로그인 페이지를 Scaffold로 만들었다고 하자

로고

먼저 로고는 벡터이미지를 이용해서 그렸다.
벡터이미지를 사용하기 위해서는 의존성을 추가해야 한다.

SVG 라이브러리

Vector이미지를 이용하기 위해서 svg라이브러리를 이용한다.
https://pub.dev/ 으로 이동
svg 이동


dependencies:
  flutter:
    sdk: flutter
  flutter_svg: ^2.0.4  # 이 녀석 추가

컴포넌트로 로고를 만든다.

class Logo extends StatelessWidget {

  final title; // 변하지 않는 글자이기 때문에 String -> final로 변경

  // Logo({required this.title ,Key? key}) : super(key: key);  선택적 매개변수를 사용한다면
  Logo(this.title,{Key? key}) : super(key: key); // 하나만 변하게 하고 싶을때 시그니처 사용

  
  Widget build(BuildContext context) {
    return Column(
      children: [
        SvgPicture.asset("assets/logo.svg", height: 70, width: 70),
        Text(title,
          style: TextStyle(fontSize: 40, fontWeight: FontWeight.bold),)
      ],
    );
  }
}

<참고> 리스트의 위치나 순서를 바꾸기 위해서는 매개변수에 key가 필요한데 아래코드를 참고하면 key를 이용해서 순서를 제어한다.

ListView(
  children: [
    Text('Item 1', key: Key('item_1')),
    Text('Item 2', key: Key('item_2')),
    Text('Item 3', key: Key('item_3')),
  ],
  onReorder: (oldIndex, newIndex) {
    setState(() {
      final item = _items.removeAt(oldIndex);
      _items.insert(newIndex, item);
    });
  },
);

Overflow 방지

로그인 페이지를 간단하게 만든다.

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: Container(
        margin: EdgeInsets.all(30),
        child: ListView(
          children: [
            Logo( 'Login'), // 컬럼은 기본 가운데
            Form(
              child: Column(
                children: [
                  SizedBox(height: 10),
                  CustomTextFormField("email"),
                  SizedBox(height: 10),
                  CustomTextFormField("password", isObscure: true),// 밑에서 올라오는 키보드는 인셋이라고 한다.
                  SizedBox(height: 20),
                  TextButton(onPressed: (){}, 
                  child: Text("Login",
                    style: TextStyle(fontSize: 20),
                  )
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }

TextFormField를 이용해서 입력을 받는다면 자동적으로 아래에서 입력할수 있게 키보드가 올라오게 되는데 이때 올라오는 키보드의 영역을 keyboard overflow라고 한다.

위에서는 ListView를 이용해서 동적으로 화면을 움직이게 만들었지만 Column으로 화면을 고정시켜서 영역을 계산하지 않고 만든다면 overflow가 올라올때 아래처럼 오버플로우에러가 발생하게 된다.

유효성 검사

CustomTextFormField를 만들어서 데이터를 입력받게 만들었다

class CustomTextFormField extends StatelessWidget {
  final title;
  // final bool? isObscure; -> bool타입은 `?` 안붙이면 오류발생한다.
  final isObscure; // 선택적 매개변수의 디폴트값 false로 지정해 `?` 제거함
  const CustomTextFormField(this.title, {this.isObscure = false, Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(title),
        TextFormField(
          validator: (value) => // validator 가 입력된 모든 데이터를 가져옴
            value!.isEmpty ? "Please enter some text" : null, // 유효성 통과하면 null 리턴
          obscureText: isObscure,
          decoration: MyInputDecoration(
            "Enter $title" // 변수 하나만 바인딩 한다면 중괄호가 굳이 필요없다.
        ),
        ),
      ],
    );
  }
}

class MyInputDecoration extends InputDecoration{
    MyInputDecoration(String hint): super(
      hintText: hint,
      // 첫번째 보더 옵션
      border: OutlineInputBorder(
        borderRadius: BorderRadius.all(Radius.circular(20)))
        
      // 위 옵션을 사용하거나 아래 옵션을 사용
      enabledBorder: OutlineInputBorder(
          borderRadius: BorderRadius.circular(20)
      ),
      focusedBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(20),
      ),
      errorBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(20),
      ),
      focusedErrorBorder: OutlineInputBorder(
        borderRadius: BorderRadius.circular(20),
      ),
    );
}

InputDecoration는 주로 TextFormField를 스타일링하는데 사용한다.

obscureText: true, 프로퍼티는 비밀번호를 마스킹해서 *로 표시해준다.

validator는 입력된 필드의 유효성을 검사해주는 함수다.
해당 폼 위젯이 submit 되거나 포커스를 잃게 되면 발동한다.

validatorString?을 리턴해야 하는데 만약 null을 리턴한다면 유효성 검사 통과를 의미한다.
삼항연산자를 사용해서 필드가 비었다면 문구를 표현하게 만들었다.

데이터가 유효 하다면 TextFormFieldonSaved 함수를 호출해서 데이터를 저장하고 서버로 날릴 준비를 하면 된다.

GlobalKey

간단하게 버튼을 눌렀을때 유효성 검사를 하려면 GlobalKey 를 로그인 필드에 추가한다.
GlobalKey 는 위젯트리내에서 전역적으로 고유한 키를 생성해주고 GlobalKey<FormState>()는 폼 위젯의 키를 준다.

final _formKey = GlobalKey<FormState>(); // 폼의 상태를 확인 -> const 제거

키를 이용해서 Form을 약간 수정한다.

Form(
   key: _formKey, // 생성한 final 글로벌 키를 연결한다.
   child: Column(
   children: [
		// 생략
   TextButton(onPressed: (){
      if(_formKey.currentState!.validate()) { 
      Navigator.pushNamed(context, "/home"); }
		// 생략

validate()를 호출할때 currentState가 null 이라면 NullThrownError가 발생할수 있기 때문에 컴파일러가 경고를 한다.
! 를 붙여서 null이 아님을 컴파일러에게 알려준다.

글로벌키를 이용해서 각각의 폼의 유효성결과가 모두 유효할때 _formKey.currentState!.validate()가 true가 되어서 다음 로직을 수행하게 된다.

Navigator.pushNamed(context, "/home");을 사용해서 홈으로 넘어가게 만들었다.
로그인을 성공하면 이전화면으로 돌아가는 경우는 없기 때문에 이전 화면은 날려서 메모리를 관리하자.

입력을 하지 않으면 텍스트필드의 validator가 String을 리턴하게 되고 폼필드 아래에 해당 메세지가 나오게 된다.

유효성 검사가 모두 통과 한다면

profile
작은것부터
post-custom-banner

0개의 댓글