목표
사용자를 등록할 수 있는 UI를 만들고 Firebase를 이용해 기능을 구현해보자.
현재 상황 정리
지난번까지 연습해본 것은
(1) 로그인 화면 구성 (https://riris01.tistory.com/35)
(2) 로그아웃 방법 (https://riris01.tistory.com/36)
(3) 현재 사용 중인 사용자 표시 (https://riris01.tistory.com/36)
를 알아보았다.
그렇다면 이번에 알아볼 것은, (4) 등록화면이 될 것이다.
Firebase를 사용을 하면서 이번에도 한번 빠르고 쉽게(!) 등록화면과 기능을 구성해보자.
github은 여기서 다운가능하다.
https://github.com/riris01/firebase_signin_name_signup
실제 구동화면
1. Login버튼 화면 아래에 Sign up 버튼을 배치하였다.
2. Sign up page의 UI 구성을 하였으며, 여기에도 TextFormField를 각각 사용을 하였다.
3. Registration 버튼을 누르면 validate 여부를 판단하여, 등록 가능여부를 확인하도록 하였다.
4. 등록이 완료가 되면, SnackBar로 성공 여부를 확인할 수 있도록 하였다.
5. 새로만든 아이디와 비밀번호로 로그인 해보면, 정상 작동하는 것을 알 수 있다.
코드설명
그럼 지금부터 코드를 하나씩 보면서 어떻게 화면을 구성하였는지 확인을 해보자.
일단 파일을 생성해야한다. 지금까지 생성한 파일은 총 4개였으며, 이번에는 sign_up.dart라는 파일을 하나 만들어준다.
(1) 파일 생성
lib
- login_page.dart
- main.dart
- market_page.dart
- my_home_page.dart
- sign_up.dart
(2) 등록 화면 만들기
화면을 구성하는 방법이야 워낙 많지만, TextFormField 두개와 Registration 버튼 하나를 두었다. 꼭 이런 모양이 아니더라도 개인의 취향대로 맘껏 각각의 박스들을 생성을 해서 작성들을 해보기를 바란다. 그렇게 해보면서 유저들이 로그인을 할때, 유저들이 편하게 로그인 할 수 있는 방법들을 디자인적으로 강구해보는 좋은 기회가 될 것이다.
이건 사견이긴 하지만, 본인이 제품 기획 관련 업무를 하면서 디자이너, 개발자 등과 함께 팀을 이루어 일을 할때, 어느 한쪽의 '편의성'에만 치우쳐서 제품을 만들다 보면, 결국 사용자의 편의성과는 전혀 거리가 먼 시장성이 없는 제품이 탄생하는 것도 간혹 발견을 하였다. 제품을 제작하기를 원한다면 사용하는 사람의 입장에서 사용할시 예상되는 불편한점 혹은 편의성들을 스스로 고민해보고 분석해서 조금씩 적용해 UI와 기능(개발)을 구현하는 것이 중요하다.
여튼, 이번에는 등록을 위해서 만든 예시이니 그런 고려사항은 쏙 뺐다.
import 'package:firebase_core/firebase_core.dart';
import 'main.dart';
import 'market_page.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SignUpPage extends StatefulWidget {
const SignUpPage({super.key});
@override
State<SignUpPage> createState() => _SignUpPageState();
}
class _SignUpPageState extends State<SignUpPage> {
Future<void> registerUser() async {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sign Up'),
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
const SizedBox(height: 20.0),
TextFormField(
decoration: InputDecoration(labelText: 'Email'),
),
const SizedBox(height: 20.0),
TextFormField(
),
SizedBox(height: 20),
Container(
height: 70,
width: double.infinity,
padding: const EdgeInsets.only(top: 8.0),
child: ElevatedButton(
onPressed: () {
},
child: const Text(
"Registration",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
}
}
이제 화면은 만들어졌으니, 기능들을 추가해보자.
(3) 선언하기
빠질 수 없는 Key 선언의 시간이다.
_fromKey를 써서 GlobalKey 선언을 통해 하위 위젯에 적용에 요소들이 적용을 하도록 만들고, _emailController와 _passwordController의 유효성을 나중에 검사 받을 수 있도록 TextEditingController와 연결하여준다. 음, 쉽게 말한다면 여기에다가 '글을 쓸터이니 _emailController라고 태그가 달려있는 이 'Email'을 나중에 검사할때 유용하게 써주세요.' 라고 미리 예약을 걸어놓는 느낌이다.
(...)
class _SignUpPageState extends State<SignUpPage> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
(...)
(4) Registration (등록)이 발생할때, 처리 되는 방식
Registration을 할때, 일일이 Firebase와 Flutter간의 연결방법에 대해서 클래스를 생성하고 구현을 할 필요가 없다. 다만 우리가 여기서 사용할 메서드는 FirebaseAuth.instance.createUserWithEmailAndPassword()이다. 이름이 참 길지만 보는 바와 같이 createUserWithEmailAndPassword 즉, 이메일과 패스워드를 사용하여 등록할 수 있도록 도와주는 메서드 이다. 그렇다면 email과 password를 무엇으로 받아서 전달을 할지 알려줘야하겠지. 그래서 _emailController와 _passwordController를 email과 password에 각각 할당하였다. 그리고 trim()이라는 것이 보일텐데, 이메일의 좌우 공백을 제거하여 글자만 전달될 수 있도록 하는 기능이다.
그 다음에는 ScaffoldMessenger를 사용하여 등록이 되면 등록이 되었다('Registered')라고 표현할 수 있도록 하였으며, 만약 그 외 문제가 발생한다면 어떤식으로 오류가 발생했는지를 보여줄 수 있는 Snackbar가 실행되도록 하였다. 예를 들어 email이 이미 사용되고 있다면 e.code == 'email-already-in-use' 작업을 거쳐 'Error', 'The email is already in use' 가 스낵바로 나타나도록 해두었다.
(...)
Future<void> registerUser() async {
try {
final userCredential =
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Registered')),
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
Get.snackbar('Error', 'The password is too weak.');
} else if (e.code == 'email-already-in-use') {
Get.snackbar('Error', 'The email is already in use.');
} else {
Get.snackbar(
'Error', 'Failed to register user. Please try again later.');
}
} catch (e) {
print('Error: $e');
Get.snackbar('Error', 'Failed to register user. Please try again later.');
}
}
(...)
(5) 화면에 각각 기능들을 삽입하기
자, 입력받을 데이터의 선언과 데이터의 처리 방식에 대해서 적었다면 이제 어디서 이메일과 비밀번호를 받을지 삽입해야하지 않겠는가?
TextFormField()안에 controller를 선언해서 이 부분에서는 email을 받기 위한 장소라는 것을 알려준다. 그리고 validator를 사용해서 value가 null 이거나 비어있으면 'please enter your email'이라는 글귀를 돌려주도록 하였다.
(...)
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email.';
}
return null;
},
(...)
비밀번호도 비슷하지만 여기에는 유효성 검사에 하나 더 추가한 것이 있다.
(...)
if (value.length < 6) {
return 'The password must be at least 6 characters long.';
}
(...)
Firebase에는 보통 비밀번호가 6자 이상이여야 한다. 그래서 비밀번호가 6자 이하이면, 경고 문구를 반환하도록 하였다.
각각의 입력 상자에는 무엇을 받고 어떤식의 유효성을 검사해야하는지 확인했으니까, 이제 버튼을 누르면 등록이 되도록 해야겠지.
(...)
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
registerUser();
}
},
child: const Text(
"Registration",
style: TextStyle(fontSize: 16),
),
(...)
_formKey의 요소를 받아와서 currentState를 확인했을때 validate()하다면, registerUser라는 메소드를 실행을 하여 등록을 완료하도록 한다. 여기에다가 등록 완료시 첫 로그인 화면으로 돌아가도록 코드를 걸어놔도 좋을 듯 하다 (해봐야겠다.)
(...)
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sign Up'),
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
const SizedBox(height: 20.0),
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email.';
}
return null;
},
),
const SizedBox(height: 20.0),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password.';
}
if (value.length < 6) {
return 'The password must be at least 6 characters long.';
}
return null;
},
),
SizedBox(height: 20),
Container(
height: 70,
width: double.infinity,
padding: const EdgeInsets.only(top: 8.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
registerUser();
}
},
child: const Text(
"Registration",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
}
(...)
전체코드 (sign_up.dart)
import 'package:firebase_core/firebase_core.dart';
import 'main.dart';
import 'market_page.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class SignUpPage extends StatefulWidget {
const SignUpPage({super.key});
@override
State<SignUpPage> createState() => _SignUpPageState();
}
class _SignUpPageState extends State<SignUpPage> {
final _formKey = GlobalKey<FormState>();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
Future<void> registerUser() async {
try {
final userCredential =
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: _emailController.text.trim(),
password: _passwordController.text,
);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Registered')),
);
} on FirebaseAuthException catch (e) {
if (e.code == 'weak-password') {
Get.snackbar('Error', 'The password is too weak.');
} else if (e.code == 'email-already-in-use') {
Get.snackbar('Error', 'The email is already in use.');
} else {
Get.snackbar(
'Error', 'Failed to register user. Please try again later.');
}
} catch (e) {
print('Error: $e');
Get.snackbar('Error', 'Failed to register user. Please try again later.');
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Sign Up'),
),
body: Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
children: [
const SizedBox(height: 20.0),
TextFormField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your email.';
}
return null;
},
),
const SizedBox(height: 20.0),
TextFormField(
controller: _passwordController,
decoration: InputDecoration(labelText: 'Password'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your password.';
}
if (value.length < 6) {
return 'The password must be at least 6 characters long.';
}
return null;
},
),
SizedBox(height: 20),
Container(
height: 70,
width: double.infinity,
padding: const EdgeInsets.only(top: 8.0),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
registerUser();
}
},
child: const Text(
"Registration",
style: TextStyle(fontSize: 16),
),
),
),
],
),
),
),
);
}
}
자, (등록화면)까지 만들어보았으니 이제 내용을 조금 더 채워넣어봐야겠다.
다음에는 (a) 등록 완료 후 로그인 화면으로 돌아가기 (b) 게시판 만들기를 해보도록 하겠다.
'Flutter' 카테고리의 다른 글
[이론] MVVM 패턴 (0) | 2023.05.02 |
---|---|
[Android]Firebase-todo (0) | 2023.04.29 |
[Android]Firebase-Logout 구현 + 로그인한 사람 표시 (0) | 2023.04.26 |
[Android] Firebase-Login 구현하기 (0) | 2023.04.25 |
[Android] Firebase - Flutter 연결하기 (0) | 2023.04.25 |