본문 바로가기
Programming/Flutter

[Flutter] Flutter 기초 #05 - 로그인 페이지 구현

by NAIMJAE 2025. 1. 14.

#05 Flutter 기초 - 로그인 페이지 구현

 

 


Ⅱ 로그인 페이지 구성 요소

01. MaterialApp의 속성

initialRoute

  • 앱 실행 시 초기 화면을 설정하는 속성
  • 초기 페이지를 문자열 경로로 지정

routes

  • 앱 내의 화면 경로와 해당 위젯을 매핑
  • key-value 쌍으로 구성된 Map 형태
  • 각 경로는 문자열 키로 설정, 해당 경로에 표시될 페이지를 위젯 생성 함수로 설정
MaterialApp(
  initialRoute: '/home',
  routes: {
    '/home': (context) => HomePage(),
    '/login': (context) => LoginPage(),
  },
);

 


02. Navigator

Navigator

  • Flutter에서 화면 간 이동을 관리하는 클래스
  • 스택(Stack) 구조를 사용해 페이지를 관리

주요 메서드

  • push() : 새로운 화면을 스택에 추가
  • pop() : 현재 화면을 스택에서 제거하고 이전 화면으로 돌아감
  • pushNamed() : 이름 기반 경로로 화면 이동
  • popAndPushNamed() : 현재 화면을 제거하고 새 화면 추가
TextButton(
  onPressed: () {
    Navigator.pushNamed(context, '/login');
  },
  child: Text('로그인 페이지로 이동'),
);

03. Form & TextFormField

Form

  • 여러 입력 필드를 하나의 Form으로 묶어 관리할 수 있도록 도와주는 위젯
  • 유효성 검사, 폼 제출 처리, 입력 값 저장 및 초기화 기능을 제공

Form의 주요 속성

  • key : 폼 상태를 관리하기 위한 GlobalKey를 사용
  • child : 폼 내부에 포함될 입력 필드 위젯
final _formKey = GlobalKey<FormState>();

Form(
  key: _formKey,
  child: Column(
    children: [
      TextFormField(
        decoration: InputDecoration(labelText: '이메일'),
      ),
      TextButton(
        onPressed: () {
          if (_formKey.currentState!.validate()) {
            print('폼 제출 성공');
          }
        },
        child: Text('제출'),
      ),
    ],
  ),
);

TextFormField

  • 사용자 텍스트 입력을 받는 위젯
  • 유효성 검사와 폼 상태 관리가 가능한 위젯

TextFormField의 주요 속성

  • decoration : 스타일 지정
  • validator : 입력 값의 유효성 검사 함수를 설정
  • keyboardType : 키보드 타입 지정
  • onSaved : 폼 제출 시 입력 값을 저장할 때 사용
TextFormField(
  decoration: InputDecoration(
    labelText: '이메일',
    hintText: '이메일을 입력하세요',
    border: OutlineInputBorder(),
  ),
  validator: (value) {
    if (value == null || value.isEmpty) {
      return '이메일을 입력해주세요';
    }
    return null;
  },
  onSaved: (value) {
    print('저장된 값: $value');
  },
);

04. GlobalKey & FormState

GlobalKey

  • Flutter의 Key 시스템에서 위젯의 상태에 대한 전역 참조를 제공
  • 이를 통해 특정 위젯의 상태에 직접 접근하고 제어할 수 있음

FormState

  • Form 위젯의 상태를 나타내는 클래스
  • 이를 통해 폼에 입력된 데이터 관리, 유효성 검사 등의 작업 가능

FormState의 주요 메서드

  • validate() : 폼의 모든 입력 필드에 대해 유효성 검사를 실행
  • save() : 입력 필드의 값을 저장
  • reset() : 폼의 상태를 초기화

GlobalKey와 FormState의 작동 원리

  • Form 위젯의 key 속성에 GlobalKey<FormState> 등록
    • Form 위젯은 key 속성을 통해 자신의 상태를 관리할 수 있는 FormState 객체와 연결
    • GlobalKey를 사용해 다른 위젯 계층에서도 상태에 접근할 수 있음
  • 런타임 시점에 Form과 그 안에 속한 TextFormField의 상태가 FormState에 저장
    • 앱이 실행되면 Form 위젯과 그 안의 모든 TextFormField 위젯들이 FormState 객체에 등록 됨
    • 이후 입력 값과 상태가 FormState 객체에서 관리 됨
  • Form 위젯의 상태 제어
    • FormState 객체에 접근하기 위해 _formKey.currentState를 사용
    • 이를 통해 유효성 검사(validate()), 입력값 저장(save()) 등의 작업 수행

Ⅱ 로그인 화면 예제

01. 페이지 생성 및 main 함수 세팅

main 함수

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      initialRoute: '/login',
      routes: {
        '/login': (context) => LoginPage(),
        '/home': (context) => HomePage(),
      },
    );
  }
}

 

로그인 페이지

class LoginPage extends StatelessWidget {

  LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            SizedBox(height: 30),
	          Text('로그인 페이지', style: TextStyle(fontSize: 30)),
          ],
        ),
      ),
    );
  }
}

 

홈 페이지

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Column(
        children: [
          SizedBox(height: 30),
          Text('홈 페이지', style: TextStyle(fontSize: 30)),
        ],
      ),
    );
  }
}

02. 로그인 페이지 사용자 입력 필드 구현

class LoginPage extends StatelessWidget {
  // GlobalKey 선언 - 폼 상태를 관리하기 위한 Key
  final _formKey = GlobalKey<FormState>();

  // 사용자가 입력한 데이터를 저장하는 멤버 변수 선언
  String _email = '';
  String _password = '';

  LoginPage({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            SizedBox(height: 30),
            Text('로그인 페이지', style: TextStyle(fontSize: 30)),
            // Form 위젯 선언
            Form(
              key: _formKey, // Form 상태 관리를 위해 생성한 _formKey
              child: Column(
                children: [
                  // email 입력 필드
                  TextFormField(
                    decoration: InputDecoration(
                      label: Text('email'),
                      border: OutlineInputBorder(),
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'email을 입력하세요.';
                      }
                      return null;
                    },
                    onSaved: (value) {
                      _email = value ?? '';
                    },
                  ),
                  SizedBox(height: 20),
                  // 비밀번호 입력 필드
                  TextFormField(
                    decoration: InputDecoration(
                      label: Text('password'),
                      border: OutlineInputBorder(),
                    ),
                    validator: (value) {
                      if (value == null || value.isEmpty) {
                        return 'password를 입력하세요.';
                      }
                      return null;
                    },
                    onSaved: (value) {
                      _password = value ?? '';
                    },
                  ),                 
                ],
              ),
            )
          ],
        ),
      ),
    );
  }
}

 

코드 설명

  • GlobalKey<FormState>()로 폼 위젯의 상태를 관리
  • TextFormField 위젯의 validator에서 입력된 값의 유효성 검사
  • TextFormField 위젯의 onSaved에서 emailpassword필드에 값 삽입

03. 로그인 버튼 구현 및 페이지 이동

로그인 페이지

(...)
body: Column(
  children: [
    SizedBox(height: 30),
    Text('로그인 페이지', style: TextStyle(fontSize: 30)),
    Form(
      key: _formKey,
      child: Column(
        children: [
          // email 입력 필드
          TextFormField(...),
          SizedBox(height: 20),
          // 비밀번호 입력 필드
          TextFormField(...),
          // 로그인 버튼
          TextButton(
            onPressed: () {
              if (_formKey.currentState!.validate()) {
                // 유효성 검사를 다 통과하면 아래 로직 호출
                print('Email : ${_email}');
                print('Password : ${_password}');

                // 유효성 검사 통과 후 Navigator를 이용한 페이지 이동
                Navigator.pushNamed(context, '/home');
              }
            },
            child: Text('LOGIN', style: TextStyle(color: Colors.black)),
          )
        ],
      ),
    )
  ],
),

 

코드 설명

  • TextButton 위젯의 onPressed에서 유효성 검증 로직 호출
  • _formKey.currentState!.validate()
    • 현재 Form에 등록된 모든 TextFormFieldvalidator 속성을 실행
    • 모든 validator가 유효하면 true를 반환하고, 그렇지 않으면 false를 반환
  • 유효성 검사를 통과하면 Navigator를 이용해 화면 이동

 

홈 페이지

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

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: Scaffold(
        body: Column(
          children: [
            SizedBox(height: 30),
            Text('홈 페이지', style: TextStyle(fontSize: 30)),
            TextButton(
              onPressed: () {
                // 버튼 누르면 Navigator를 이용한 페이지 이동
                Navigator.pop(context);
              },
              child: Text('로그인 페이지로 이동'),
            )
          ],
        ),
      ),
    );
  }
}