import 'dart:convert';
import 'package:dancemate_app/database/model.dart';
import 'package:dancemate_app/widgets/validation.dart';
import 'package:dancemate_app/google_sign_in_service.dart';
import 'package:dancemate_app/provider/main_tap_provider.dart';
import 'package:dancemate_app/provider/user_provider.dart';
import 'package:dancemate_app/screens/search_user_screen.dart';
import 'package:dancemate_app/screens/signup_screen.dart';
import 'package:dancemate_app/widgets/error.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:dancemate_app/screens/main_tab_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:kakao_flutter_sdk/kakao_flutter_sdk_talk.dart';
import 'package:kakao_flutter_sdk/kakao_flutter_sdk_template.dart';
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
class LoginScreen extends ConsumerStatefulWidget {
const LoginScreen({
super.key,
});
@override
ConsumerState<LoginScreen> createState() => _LoginScreenState();
}
class _LoginScreenState extends ConsumerState<LoginScreen> {
final TextEditingController emailController = TextEditingController();
final TextEditingController passwordController = TextEditingController();
bool isValidEmail = true;
bool isEmailFieldTouched = false;
bool isValidPassword = true;
bool isPasswordFieldTouched = false;
@override
void initState() {
super.initState();
emailController.addListener(_validateEmail);
passwordController.addListener(_validatePassword);
}
void _validateEmail() {
isEmailFieldTouched = true;
final bool currentValidation = isEmailValid(emailController.text);
setState(() {
isValidEmail = currentValidation;
});
}
void _validatePassword() {
isPasswordFieldTouched = true;
final bool currentValidation = isPasswordValid(passwordController.text);
setState(() {
isValidPassword = currentValidation;
});
}
@override
void dispose() {
emailController.removeListener(_validateEmail);
passwordController.removeListener(_validatePassword);
emailController.dispose();
passwordController.dispose();
super.dispose();
}
void onClearTap(TextEditingController controller) {
controller.clear();
}
void onLoginTap() async {
_validateEmail();
_validatePassword();
if (!isValidEmail || !isValidPassword) {
errorAlert(context, '이메일 혹은 비밀번호를 확인해주세요.');
return;
}
List<dynamic> args = [
emailController.text,
passwordController.text,
];
final result = await ref.watch(postUserLoginProvider(args).future);
if (result['result_code'] == 200) {
const storage = FlutterSecureStorage();
await storage.delete(key: 'login');
final userId = result['user_id'];
final userType = result['type'];
final accessToken = result['access_token'];
final payload = jsonEncode({
'userId': userId,
'userType': userType,
'email': emailController.text,
'access_token': accessToken,
});
await storage.write(
key: 'login',
value: payload,
);
ref.read(mainTapProvider.notifier).update((state) => 0);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
} else {
errorAlert(context, result['result_msg']);
}
}
void onSocialLoginTap(int method) async {
await dotenv.load();
final String privateKey = [
dotenv.env['APPLE_PRIVATE_KEY_LINE1']!,
dotenv.env['APPLE_PRIVATE_KEY_LINE2']!,
dotenv.env['APPLE_PRIVATE_KEY_LINE3']!,
dotenv.env['APPLE_PRIVATE_KEY_LINE4']!,
dotenv.env['APPLE_PRIVATE_KEY_LINE5']!,
dotenv.env['APPLE_PRIVATE_KEY_LINE6']!,
].join('\\n');
if (method == 2) {
final GoogleSignInService signInService = GoogleSignInService();
final account = await signInService.signInWithGoogle();
if (account != null) {
if (account.additionalUserInfo != null) {
final isNewUser = account.additionalUserInfo!.isNewUser;
final credential = '${account.credential!.token}';
final userData = account.additionalUserInfo!.profile;
final email = userData!['email'];
final password = '${account.credential!.token}';
final nickname = userData['given_name'];
final name = userData['name'];
final imageUrl = userData['picture'];
const phone = '';
const introduction = '';
if (isNewUser) {
UserModel user = UserModel(
type: 1,
method: method,
email: email,
password: password,
nickname: nickname,
name: name,
phone: phone,
introduction: introduction,
imageUrl: imageUrl,
appleToken: '',
appleIdentifier: '',
);
final result = await ref.watch(postUserJoinProvider(user).future);
if (result['result_code'] == 200) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
} else {
errorAlert(context, result['result_msg']);
}
} else {
List<dynamic> args = [
email,
credential,
];
final result = await ref.watch(postUserLoginProvider(args).future);
if (result['result_code'] == 200) {
ref.read(mainTapProvider.notifier).update((state) => 0);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
}
}
}
}
} else if (method == 3) {
bool installed = await isKakaoTalkInstalled();
OAuthToken token = installed
? await UserApi.instance.loginWithKakaoTalk()
: await UserApi.instance.loginWithKakaoAccount();
User user = await UserApi.instance.me();
final id = user.id.toString();
final email = user.kakaoAccount!.email.toString();
final nickname = user.properties!['nickname'].toString();
final imageUrl = user.properties!['profile_image'].toString();
UserModel userData = UserModel(
type: 1,
method: method,
email: email,
password: id,
nickname: nickname,
name: nickname,
phone: '',
introduction: '',
imageUrl: imageUrl,
appleToken: '',
appleIdentifier: '',
);
try {
final result = await ref.watch(postUserJoinProvider(userData).future);
if (result['result_code'] == 200) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
} else {
print(result['result_msg']);
errorAlert(context, result['result_msg']);
}
} catch (e) {
final resultCode = e.toString().split(' ')[1];
if (resultCode == '207') {
List<dynamic> args = [
email,
id,
];
final loginResult =
await ref.watch(postUserLoginProvider(args).future);
if (loginResult['result_code'] == 200) {
ref.read(mainTapProvider.notifier).update((state) => 0);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
} else {
print(loginResult['result_msg']);
errorAlert(context, loginResult['result_msg']);
}
}
}
} else if (method == 4) {
final credential = await SignInWithApple.getAppleIDCredential(
scopes: [
AppleIDAuthorizationScopes.email,
AppleIDAuthorizationScopes.fullName,
],
);
final email = credential.email ?? '';
final password = credential.userIdentifier ?? '';
final nickname = credential.givenName ?? '';
final appleToken = credential.identityToken.toString();
final appleIdentifier = credential.userIdentifier ?? '';
if (email == '') {
List<dynamic> args = [
password,
password,
];
final loginResult = await ref.watch(postUserLoginProvider(args).future);
if (loginResult['result_code'] == 200) {
ref.read(mainTapProvider.notifier).update((state) => 0);
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
} else {
print(loginResult['result_msg']);
errorAlert(context, loginResult['result_msg']);
}
} else {
UserModel userData = UserModel(
type: 1,
method: method,
email: email,
password: password,
nickname: nickname,
name: nickname,
phone: '',
introduction: '',
imageUrl: '',
appleToken: appleToken,
appleIdentifier: appleIdentifier,
);
final result = await ref.watch(postUserJoinProvider(userData).future);
if (result['result_code'] == 200) {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const MainNavigationScreen(),
),
);
} else {
print(result['result_msg']);
errorAlert(context, result['result_msg']);
}
}
}
}
void onChangePasswordTap() {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SearchUserScreen(),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.symmetric(
vertical: 20,
horizontal: 50,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const SizedBox(height: 130),
SizedBox(
width: 200,
height: 120,
child: Center(
child: Image.asset(
'assets/images/app_logo/detail_2x.png',
),
),
),
const SizedBox(height: 80),
const Row(
children: [
Text(
'이메일',
style: TextStyle(
fontSize: 15,
),
),
],
),
const SizedBox(height: 5),
TextField(
controller: emailController,
decoration: InputDecoration(
hintText: 'Enter your email',
errorText: isEmailFieldTouched &&
emailController.text.isNotEmpty &&
!isValidEmail
? '이메일 형식을 입력해주세요'
: null,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1.0,
),
),
suffixIcon: GestureDetector(
onTap: () {
onClearTap(emailController);
},
child: const Icon(
Icons.cancel_outlined,
color: Colors.black54,
),
),
),
),
const SizedBox(height: 30),
const Row(
children: [
Text(
'비밀번호',
style: TextStyle(
fontSize: 15,
),
),
],
),
const SizedBox(height: 5),
TextField(
controller: passwordController,
obscureText: true,
decoration: InputDecoration(
hintText: 'Enter your password',
errorText: isPasswordFieldTouched &&
passwordController.text.isNotEmpty &&
!isValidPassword
? '비밀번호는 8~15자 이내로 입력해주세요'
: null,
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1.0,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Colors.grey.shade400,
width: 1.0,
),
),
suffixIcon: GestureDetector(
onTap: () {
onClearTap(passwordController);
},
child: const Icon(
Icons.cancel_outlined,
color: Colors.black54,
),
),
),
),
const SizedBox(height: 40),
TextButton(
onPressed: onLoginTap,
style: TextButton.styleFrom(
backgroundColor: const Color(0xFFA48AFF),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(5),
),
),
child: const Padding(
padding: EdgeInsets.symmetric(
vertical: 5,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'로그인',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
),
),
),
const SizedBox(height: 10),
Row(
children: [
GestureDetector(
onTap: onChangePasswordTap,
child: const Text(
'비밀번호를 잊으셨나요?',
style: TextStyle(
color: Colors.redAccent,
fontSize: 15,
),
),
),
],
),
const SizedBox(height: 30),
Row(
children: [
const Text(
"Don't have an account?",
style: TextStyle(
fontSize: 15,
),
),
const SizedBox(width: 5),
GestureDetector(
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => const SignUpScreen(),
),
);
},
child: const Text(
'회원가입',
style: TextStyle(
color: Color(0xFFA48AFF),
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
),
],
),
const SizedBox(height: 60),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'소셜 로그인',
style: TextStyle(
color: Colors.grey.shade500,
fontWeight: FontWeight.bold,
fontSize: 15,
),
),
],
),
const SizedBox(height: 15),
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 15,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
GestureDetector(
onTap: () {
onSocialLoginTap(3);
},
child: SizedBox(
width: 60,
height: 60,
child: Image.asset('assets/images/kakao_logo.png'),
),
),
GestureDetector(
onTap: () {
onSocialLoginTap(2);
},
child: SizedBox(
width: 60,
height: 60,
child: Image.asset('assets/images/google_logo.png'),
),
),
GestureDetector(
onTap: () {
onSocialLoginTap(4);
},
child: SizedBox(
width: 60,
height: 60,
child: Image.asset('assets/images/apple_logo.png'),
),
),
],
),
),
],
),
),
),
);
}
}