Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ feat: 일반로그인 구현 #7

Merged
merged 15 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions lib/services/auth/auth_service.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// auth_service.dart

import 'package:firebase_auth/firebase_auth.dart';
import 'package:get/get.dart';
import 'package:google_sign_in/google_sign_in.dart';

class AuthService {
final FirebaseAuth _auth = FirebaseAuth.instance;

Future<void> signInWithGoogle() async {
try {
// 구글 로그인
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();

// 구글 로그인 인증 정보
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;

// 구글 로그인 자격 증명
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);
// 구글 로그인 성공시 UserCredential 반환
await _auth.signInWithCredential(credential);

// 홈화면으로 이동
Get.offAllNamed('/');
} catch (error) {
// 에러 발생시 에러 메시지 출력
Get.snackbar(
"구글 로그인 실패",
"구글 로그인에 실패했습니다. 다시 시도해주세요.",
snackPosition: SnackPosition.TOP,
);
}
}
}
2 changes: 1 addition & 1 deletion lib/utilities/style/color_styles.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import 'package:flutter/material.dart';

class ColorStyles {
class ColorSystem {
static const Color main = Color(0xFF1DA1FA);
static const Color sub1 = Color(0xFF5588FD);
static const Color sub2 = Color(0xFFC1D2FF);
Expand Down
31 changes: 31 additions & 0 deletions lib/utilities/validators/auth_validators.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
class AuthValidators {
// Email Validator
static String? emailValidator(String? value) {
if (value == null || value.isEmpty) {
return '이메일 주소를 입력해주세요';
}
// Email 정규식
final RegExp emailRegex = RegExp(
r'^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$',
);

if (!emailRegex.hasMatch(value)) {
return '올바른 이메일 주소를 입력해주세요';
}
return null;
}

// Password Validator
// 대충.. 10자리 이상, 문자와 숫자가 섞여있어야 함
static String? passwordValidator(String? value) {
if (value == null || value.isEmpty) {
return '비밀번호를 입력해주세요';
} else if (value.length < 10) {
return '비밀번호는 10자리 이상이어야 합니다';
} else if (!RegExp(r'^(?=.*?[a-zA-Z])(?=.*?[0-9]).{10,}$')
.hasMatch(value)) {
return '비밀번호는 문자와 숫자가 섞여있어야 합니다';
}
return null;
}
}
39 changes: 39 additions & 0 deletions lib/viewModels/auth/email_login_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// email_login_viewmodel.dart

import 'package:earlips/utilities/validators/auth_validators.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class EmailLoginViewModel extends GetxController {
final formKey = GlobalKey<FormState>();
final emailController = TextEditingController();
final passwordController = TextEditingController();

// Email Validator
String? emailValidator(String? value) {
return AuthValidators.emailValidator(value);
}

// Password Validator
String? passwordValidator(String? value) {
return AuthValidators.passwordValidator(value);
}

// 로그인 메소드
Future<void> signInWithEmailAndPassword() async {
if (formKey.currentState!.validate()) {
try {
await FirebaseAuth.instance.signInWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
// 로그인 성공시 홈화면으로 이동
Get.offAllNamed('/');
} on FirebaseAuthException catch (_) {
Get.snackbar('로그인 실패', "로그인에 실패했습니다. 다시 시도해주세요.",
snackPosition: SnackPosition.TOP);
}
}
}
}
36 changes: 36 additions & 0 deletions lib/viewModels/auth/email_signup_viewmodel.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:earlips/utilities/validators/auth_validators.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

class EmailSignupViewModel extends GetxController {
final formKey = GlobalKey<FormState>();
final emailController = TextEditingController();
final passwordController = TextEditingController();

// Email Validator
String? emailValidator(String? value) {
return AuthValidators.emailValidator(value);
}

// Password Validator
String? passwordValidator(String? value) {
return AuthValidators.passwordValidator(value);
}

Future<void> registerWithEmailAndPassword() async {
if (formKey.currentState!.validate()) {
try {
await FirebaseAuth.instance.createUserWithEmailAndPassword(
email: emailController.text.trim(),
password: passwordController.text.trim(),
);
// 뒤로
Get.back();
} on FirebaseAuthException catch (_) {
Get.snackbar('회원가입 실패', '회원가입에 실패했습니다. 다시 시도해주세요.',
snackPosition: SnackPosition.TOP);
}
}
}
}
54 changes: 54 additions & 0 deletions lib/views/auth/email_login_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import 'package:earlips/viewModels/auth/email_login_viewmodel.dart';
import 'package:earlips/views/auth/email_signup_screen.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

@override
Widget build(BuildContext context) {
// 이메일 로그인 뷰 모델을 가져옴
final controller = Get.put(EmailLoginViewModel());

return Scaffold(
appBar: AppBar(
title: const Text("이메일 로그인"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Form(
// 키 값을 ViewModel에서 가져옴
key: controller.formKey,
child: Column(
children: [
TextFormField(
controller: controller.emailController,
decoration: const InputDecoration(hintText: '이메일'),
validator: (value) => controller.emailValidator(value),
),
TextFormField(
controller: controller.passwordController,
obscureText: true,
decoration: const InputDecoration(hintText: '비밀번호'),
validator: (value) => controller.passwordValidator(value),
),
const SizedBox(height: 20),
ElevatedButton(
// 로그인 메소드 호출
onPressed: controller.signInWithEmailAndPassword,
child: const Text('Login'),
),
const SizedBox(height: 10),
// 회원가입 화면으로 이동
TextButton(
onPressed: () => Get.to(() => const EmailSignupScreen()),
child: const Text("회원가입 하러가기"),
),
],
),
),
),
);
}
}
49 changes: 49 additions & 0 deletions lib/views/auth/email_signup_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import 'package:earlips/viewModels/auth/email_signup_viewmodel.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

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

@override
Widget build(BuildContext context) {
// 뷰 모델을 가져옴
final controller = Get.put(EmailSignupViewModel());

return Scaffold(
appBar: AppBar(
title: const Text("이메일 회원가입"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Form(
// 키 값을 ViewModel에서 가져옴
key: controller.formKey,
child: Column(
children: [
TextFormField(
// 이메일 메소드 호출
controller: controller.emailController,
decoration: const InputDecoration(hintText: '이메일'),
validator: (value) => controller.emailValidator(value),
),
TextFormField(
// 비밀번호 메소드 호출
controller: controller.passwordController,
obscureText: true,
decoration: const InputDecoration(hintText: '비밀번호'),
validator: (value) => controller.passwordValidator(value),
),
const SizedBox(height: 20),
ElevatedButton(
// 회원가입 메소드 호출
onPressed: controller.registerWithEmailAndPassword,
child: const Text('회원 가입'),
),
],
),
),
),
);
}
}
55 changes: 19 additions & 36 deletions lib/views/auth/login.dart → lib/views/auth/login_screen.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:earlips/services/auth/auth_service.dart';
import 'package:earlips/views/auth/email_login_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_svg/svg.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:get/get.dart';

class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
Expand All @@ -11,21 +12,23 @@ class LoginScreen extends StatefulWidget {
}

class _LoginScreenState extends State<LoginScreen> {
// AuthService 인스턴스 생성
final AuthService _authService = AuthService();

@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Expanded(
child: Column(
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// --------------------- 구글 로그인 ---------------------
InkWell(
onTap: () {
// tap
signInWithGoogle();
_authService.signInWithGoogle();
},
child: Card(
margin: const EdgeInsets.fromLTRB(20, 20, 20, 0),
Expand All @@ -47,39 +50,19 @@ class _LoginScreenState extends State<LoginScreen> {
],
),
),
)
),
const SizedBox(height: 20),
// --------------------- 이메일 로그인 ---------------------
ElevatedButton(
onPressed: () {
Get.to(() => const EmailLoginScreen());
},
child: const Text("이메일 로그인"),
),
],
))
)
],
)),
);
}

void signInWithGoogle() async {
// Trigger the authentication flow
final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();

// Obtain the auth details from the request
final GoogleSignInAuthentication? googleAuth =
await googleUser?.authentication;

// Create a new credential
final credential = GoogleAuthProvider.credential(
accessToken: googleAuth?.accessToken,
idToken: googleAuth?.idToken,
);

// Once signed in, return the UserCredential
return await FirebaseAuth.instance
.signInWithCredential(credential)
.then((value) {
print(value.user?.displayName);
print(value.user?.email);
print(value.user?.photoURL);
}).onError(
(error, stackTrace) {
print(error);
},
);
}
}
39 changes: 39 additions & 0 deletions lib/views/auth/logout_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';

Future<void> showLogoutDialog(BuildContext context) async {
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('로그아웃'),
content: const SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('정말 로그아웃하시겠습니까?'),
],
),
),
actions: <Widget>[
TextButton(
child: const Text('취소'),
onPressed: () {
Get.back();
},
),
TextButton(
child: const Text('로그아웃'),
onPressed: () async {
// 로그아웃 처리
// FirebaseAuth.instance.signOut();
await FirebaseAuth.instance.signOut();
Get.back();
},
),
],
);
},
);
}
Loading
Loading