애플로그인 구현 1차 - 회원가입만 우선
This commit is contained in:
parent
6e45010369
commit
6136ee2a3c
@ -63,9 +63,16 @@ flutter {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// 기존 의존성 ...
|
||||
// 기존 의존성 유지
|
||||
implementation 'com.google.android.gms:play-services-auth:20.6.0'
|
||||
implementation 'com.google.android.gms:play-services-ads:23.6.0'
|
||||
|
||||
// Firebase BOM
|
||||
implementation platform('com.google.firebase:firebase-bom:32.7.1')
|
||||
|
||||
// Firebase 제품들은 버전을 지정하지 않아도 됩니다
|
||||
implementation 'com.google.firebase:firebase-auth'
|
||||
implementation 'com.google.firebase:firebase-analytics'
|
||||
}
|
||||
|
||||
// (Firebase Auth, Crashlytics 등을 사용한다면, 아래 구문이 필요할 수 있습니다.)
|
||||
|
@ -14,6 +14,14 @@
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "452355332155-jcnodseb4t2bgq2mo1a6h3bn4ncp5o8c.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
"android_info": {
|
||||
"package_name": "com.allscore_app",
|
||||
"certificate_hash": "0f5c83605937a4aa55d6097253473b285bb18418"
|
||||
}
|
||||
},
|
||||
{
|
||||
"client_id": "452355332155-lqtr6l2qg0pn62vi5nccggldmcm21qdl.apps.googleusercontent.com",
|
||||
"client_type": 1,
|
||||
@ -48,10 +56,10 @@
|
||||
"client_type": 3
|
||||
},
|
||||
{
|
||||
"client_id": "452355332155-fo49j1u3qfup1sa3gj33bko6q269pqo4.apps.googleusercontent.com",
|
||||
"client_id": "452355332155-26g42j6p8kfdeknncqfmt7sitvvj611l.apps.googleusercontent.com",
|
||||
"client_type": 2,
|
||||
"ios_info": {
|
||||
"bundle_id": "com.example.allscoreApp"
|
||||
"bundle_id": "com.allscore"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
@ -9,7 +9,8 @@
|
||||
<application
|
||||
android:label="ALLSCORE"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:usesCleartextTraffic="true">
|
||||
|
||||
<!-- ★ 여기에 meta-data 추가 ★ -->
|
||||
<meta-data
|
||||
@ -43,6 +44,19 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- 여기에 애플 로그인 콜백 activity 추가 -->
|
||||
<activity
|
||||
android:name="com.aboutyou.dart_packages.sign_in_with_apple.SignInWithAppleCallback"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="signinwithapple" />
|
||||
<data android:path="callback" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
|
4
android/app/src/main/res/values/strings.xml
Normal file
4
android/app/src/main/res/values/strings.xml
Normal file
@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="default_web_client_id">1:452355332155:web:5407f79d500818e713e41e</string>
|
||||
</resources>
|
@ -26,12 +26,18 @@
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.452355332155-26g42j6p8kfdeknncqfmt7sitvvj611l</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.452355332155-26g42j6p8kfdeknncqfmt7sitvvj611l</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.allscore</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(FLUTTER_BUILD_NUMBER)</string>
|
||||
@ -79,5 +85,9 @@
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>com.apple.developer.applesignin</key>
|
||||
<array>
|
||||
<string>Default</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
398
lib/dialogs/signup_dialog.dart
Normal file
398
lib/dialogs/signup_dialog.dart
Normal file
@ -0,0 +1,398 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../views/login/signup_page.dart';
|
||||
|
||||
/* 구글 로그인 */
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
|
||||
/* 애플 로그인 */
|
||||
import 'package:sign_in_with_apple/sign_in_with_apple.dart';
|
||||
import 'dart:io' show Platform;
|
||||
import 'dart:math';
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
|
||||
/* 안내 모달창 */
|
||||
import 'response_dialog.dart';
|
||||
|
||||
/* 우리의 Api 모듈 */
|
||||
import '../plugins/api.dart';
|
||||
|
||||
/* 설정 */
|
||||
import '../config/config.dart';
|
||||
|
||||
// 회원가입 방법 선택 모달 위젯
|
||||
class SignUpDialog extends StatefulWidget {
|
||||
const SignUpDialog({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<SignUpDialog> createState() => _SignUpDialogState();
|
||||
}
|
||||
|
||||
class _SignUpDialogState extends State<SignUpDialog> {
|
||||
// 구글 로그인 객체
|
||||
final GoogleSignIn _googleSignIn = GoogleSignIn(scopes: <String>['email']);
|
||||
|
||||
// 로딩 상태 관리
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Dialog(
|
||||
backgroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
width: 300,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 제목
|
||||
const Text(
|
||||
'Sign Up Method',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// 이메일 회원가입 버튼
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
Navigator.pushAndRemoveUntil(
|
||||
context,
|
||||
MaterialPageRoute(builder: (_) => const SignUpPage()),
|
||||
(route) => false,
|
||||
);
|
||||
},
|
||||
child: const Text(
|
||||
'Sign up with Email',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// 구글 회원가입 버튼
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
icon: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/icons8-google-logo-48.png'),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'Sign up with Google',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
onPressed: _googleSignUp,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// 애플 회원가입 버튼 - iOS에서만 표시
|
||||
if (!Platform.isAndroid)
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
icon: const Icon(
|
||||
Icons.apple,
|
||||
size: 24,
|
||||
color: Colors.black,
|
||||
),
|
||||
label: const Text(
|
||||
'Sign up with Apple',
|
||||
style: TextStyle(fontSize: 16),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
onPressed: () {
|
||||
_appleSignUp();
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// 로딩 인디케이터
|
||||
if (_isLoading)
|
||||
Container(
|
||||
color: Colors.black54,
|
||||
alignment: Alignment.center,
|
||||
child: const CircularProgressIndicator(color: Colors.white),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// (D3) 구글 회원가입
|
||||
// ─────────────────────────────────────────
|
||||
Future<void> _googleSignUp() async {
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
final agreed = await _showTermsModal();
|
||||
if (agreed != true) {
|
||||
setState(() => _isLoading = false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// (2) 구글 계정 선택
|
||||
final GoogleSignInAccount? googleUser = await _googleSignIn.signIn();
|
||||
if (googleUser == null) {
|
||||
// 사용자가 회원가입 창에서 취소
|
||||
return;
|
||||
}
|
||||
|
||||
// (3) 구글 인증 정보
|
||||
final GoogleSignInAuthentication googleAuth = await googleUser.authentication;
|
||||
|
||||
// (4) FirebaseAuth Credential
|
||||
final AuthCredential credential = GoogleAuthProvider.credential(
|
||||
accessToken: googleAuth.accessToken,
|
||||
idToken: googleAuth.idToken,
|
||||
);
|
||||
|
||||
// (5) FirebaseAuth로 "회원가입" 시도
|
||||
final UserCredential userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
|
||||
final User? user = userCredential.user;
|
||||
if (user == null) {
|
||||
showResponseDialog(context, 'Error' /* 오류 */, 'Google account authentication failed.'
|
||||
/* 구글계정 인증에 실패했습니다. */);
|
||||
return;
|
||||
}
|
||||
|
||||
// (6) idToken 추출 후, 서버에 회원가입 요청
|
||||
final idToken = await user.getIdToken();
|
||||
final requestBody = {
|
||||
'id_token': idToken,
|
||||
};
|
||||
|
||||
final response = await Api.serverRequest(uri: '/user/google/signup', body: requestBody);
|
||||
if (response['result'] == 'OK') {
|
||||
final resp = response['response'] ?? {};
|
||||
if (resp['result'] == 'OK') {
|
||||
// 회원가입 성공 안내
|
||||
showResponseDialog(context, 'Sign-up Complete' /* 회원가입 완료 */, 'Google sign-up has been completed.'
|
||||
/* 구글 회원가입이 완료되었습니다. */);
|
||||
} else {
|
||||
// 실패 시
|
||||
final msgTitle = resp['response_info']?['msg_title'] ?? 'Error' /* 오류 */;
|
||||
final msgContent = resp['response_info']?['msg_content'] ?? 'Failed to sign up.'
|
||||
/* 회원가입에 실패했습니다. */;
|
||||
showResponseDialog(context, msgTitle, msgContent);
|
||||
}
|
||||
} else {
|
||||
showResponseDialog(context, 'Error' /* 오류 */, 'Google sign-up request failed.'
|
||||
/* 구글 회원가입 요청 실패 */);
|
||||
}
|
||||
} catch (e) {
|
||||
showResponseDialog(context, 'Error' /* 오류 */, 'An error occurred during Google sign-up.\n$e'
|
||||
/* 구글 회원가입 중 오류가 발생했습니다.\n$e */);
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// (D4) 애플 회원가입
|
||||
// ─────────────────────────────────────────
|
||||
Future<void> _appleSignUp() async {
|
||||
// 안드로이드 기기 체크
|
||||
if (Platform.isAndroid) {
|
||||
showResponseDialog(
|
||||
context,
|
||||
'사용 불가', // 제목
|
||||
'애플 회원가입은 안드로이드 기기에서 제공되지 않습니다.', // 내용
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
setState(() => _isLoading = true);
|
||||
|
||||
final agreed = await _showTermsModal();
|
||||
if (agreed != true) {
|
||||
setState(() => _isLoading = false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 1. 애플 로그인 credential 획득
|
||||
final rawNonce = generateNonce();
|
||||
final nonce = sha256ofString(rawNonce);
|
||||
|
||||
final appleCredential = await SignInWithApple.getAppleIDCredential(
|
||||
scopes: [
|
||||
AppleIDAuthorizationScopes.email,
|
||||
AppleIDAuthorizationScopes.fullName,
|
||||
],
|
||||
);
|
||||
print('appleCredential: $appleCredential');
|
||||
|
||||
// 2. Firebase credential 생성
|
||||
final oauthProvider = OAuthProvider('apple.com');
|
||||
print('oauthProvider: $oauthProvider');
|
||||
final credential = oauthProvider.credential(
|
||||
idToken: appleCredential.identityToken,
|
||||
accessToken: appleCredential.authorizationCode,
|
||||
);
|
||||
print('credential: $credential');
|
||||
|
||||
// 3. Firebase 인증
|
||||
final userCredential = await FirebaseAuth.instance.signInWithCredential(credential);
|
||||
print('userCredential: $userCredential');
|
||||
final user = userCredential.user;
|
||||
print('user: $user');
|
||||
|
||||
if (user == null) {
|
||||
showResponseDialog(context, 'Error', 'Apple account authentication failed.');
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 서버에 회원가입 요청
|
||||
final idToken = await user.getIdToken();
|
||||
final requestBody = {
|
||||
'id_token': idToken,
|
||||
};
|
||||
|
||||
final response = await Api.serverRequest(uri: '/user/apple/signup', body: requestBody);
|
||||
if (response['result'] == 'OK') {
|
||||
final resp = response['response'] ?? {};
|
||||
if (resp['result'] == 'OK') {
|
||||
showResponseDialog(
|
||||
context,
|
||||
'Sign-up Complete',
|
||||
'Apple sign-up has been completed.'
|
||||
);
|
||||
} else {
|
||||
final msgTitle = resp['response_info']?['msg_title'] ?? 'Error';
|
||||
final msgContent = resp['response_info']?['msg_content'] ?? 'Failed to sign up.';
|
||||
showResponseDialog(context, msgTitle, msgContent);
|
||||
}
|
||||
} else {
|
||||
showResponseDialog(
|
||||
context,
|
||||
'Error',
|
||||
'Apple sign-up request failed.'
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
showResponseDialog(
|
||||
context,
|
||||
'Error',
|
||||
'An error occurred during Apple sign-up.\n$e'
|
||||
);
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
// nonce 생성 유틸리티 함수들 추가
|
||||
String generateNonce([int length = 32]) {
|
||||
const charset = '0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._';
|
||||
final random = Random.secure();
|
||||
return List.generate(length, (_) => charset[random.nextInt(charset.length)]).join();
|
||||
}
|
||||
|
||||
String sha256ofString(String input) {
|
||||
final bytes = utf8.encode(input);
|
||||
final digest = sha256.convert(bytes);
|
||||
return digest.toString();
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// (E) 약관 모달 (개인정보 수집 동의)
|
||||
// ─────────────────────────────────────────
|
||||
Future<bool?> _showTermsModal() async {
|
||||
return showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (ctx) {
|
||||
return AlertDialog(
|
||||
backgroundColor: Colors.white,
|
||||
title: const Text(
|
||||
'Privacy Collection and Usage Agreement'
|
||||
/* 개인정보 수집 및 이용 동의서 */,
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: const [
|
||||
Text(
|
||||
Config.termsOfService,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () => Navigator.pop(ctx, false),
|
||||
child: Text('Disagree'
|
||||
/* 거부 */),
|
||||
),
|
||||
TextButton(
|
||||
style: TextButton.styleFrom(
|
||||
backgroundColor: Colors.black,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
onPressed: () => Navigator.pop(ctx, true),
|
||||
child: Text('Agree'
|
||||
/* 동의 */),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -4,6 +4,9 @@ import 'dart:convert' show utf8, jsonEncode;
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
// 회원가입 모달창
|
||||
import '../../dialogs/signup_dialog.dart';
|
||||
|
||||
/* 우리의 Api 모듈 */
|
||||
import '../../plugins/api.dart';
|
||||
|
||||
@ -18,6 +21,7 @@ import 'signup_page.dart'; /* 회원가입 페이지 임포트 */
|
||||
/* 구글 로그인 */
|
||||
import 'package:google_sign_in/google_sign_in.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'dart:io' show Platform;
|
||||
|
||||
/* 광고 */
|
||||
import 'package:google_mobile_ads/google_mobile_ads.dart';
|
||||
@ -140,7 +144,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
if (resp['result'] == 'OK') {
|
||||
/* 로그인 성공 */
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('oauth_type', 'idpw'); // /* google_user_yn = N 대신 */
|
||||
await prefs.setString('oauth_type', 'idpw');
|
||||
await prefs.setBool('auto_login', true);
|
||||
await prefs.setString('jwt_token', resp['auth']['token'].toString());
|
||||
await prefs.setInt('my_user_seq', resp['auth']['user_seq']);
|
||||
@ -208,7 +212,7 @@ class _LoginPageState extends State<LoginPage> {
|
||||
final resp = response['response'] ?? {};
|
||||
if (resp['result'] == 'OK') {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('oauth_type', 'google'); // /* google_user_yn = Y 대체 */
|
||||
await prefs.setString('oauth_type', 'google');
|
||||
await prefs.setBool('auto_login', true);
|
||||
await prefs.setString('jwt_token', resp['auth']['token'].toString());
|
||||
await prefs.setInt('my_user_seq', resp['auth']['user_seq']);
|
||||
@ -221,10 +225,6 @@ class _LoginPageState extends State<LoginPage> {
|
||||
showResponseDialog(context, 'Error' /* 오류 */, 'Google login request failed.'
|
||||
/* 구글 로그인 요청 실패 */);
|
||||
}
|
||||
|
||||
// (optional) SharedPreferences에 google_user_yn = 'Y' 저장 등
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('google_user_yn', 'Y');
|
||||
} catch (e) {
|
||||
_showAlert('Error' /* 오류 */, 'An error occurred during Google login.\n$e'
|
||||
/* 구글 로그인 중 오류가 발생했습니다.\n$e */);
|
||||
@ -233,6 +233,22 @@ class _LoginPageState extends State<LoginPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// (D4) 애플 로그인 (임시)
|
||||
// ─────────────────────────────────────────
|
||||
Future<void> _appleLogin() async {
|
||||
setState(() => _isLoading = true);
|
||||
try {
|
||||
// TODO: 애플 로그인 구현
|
||||
showResponseDialog(context, 'Notice', 'Apple login will be implemented soon.');
|
||||
} catch (e) {
|
||||
showResponseDialog(context, 'Error', 'An error occurred during Apple login.\n$e');
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────
|
||||
// (D3) 구글 회원가입
|
||||
// ─────────────────────────────────────────
|
||||
@ -529,13 +545,18 @@ class _LoginPageState extends State<LoginPage> {
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.push(context, MaterialPageRoute(builder: (_) => const SignUpPage()));
|
||||
// Navigator.push(context, MaterialPageRoute(builder: (_) => const SignUpPage()));
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (_) => const SignUpDialog(),
|
||||
);
|
||||
},
|
||||
child: const Text(
|
||||
'Sign Up'
|
||||
/* 회원가입 */,
|
||||
style: TextStyle(color: Colors.black),
|
||||
),
|
||||
|
||||
),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
@ -546,71 +567,74 @@ class _LoginPageState extends State<LoginPage> {
|
||||
// 2. 구글 로그인 / 회원가입
|
||||
// ─────────────────────────────────────────
|
||||
const Text(
|
||||
'Google Account'
|
||||
'Social Login'
|
||||
/* 구글 계정 */,
|
||||
style: TextStyle(fontSize: 20, color: Colors.black, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// (a) 구글 로그인
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: ElevatedButton.icon(
|
||||
icon: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/icons8-google-logo-48.png'),
|
||||
fit: BoxFit.contain,
|
||||
// 소셜 로그인 버튼들을 Row로 배치
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
// 구글 로그인 버튼
|
||||
Container(
|
||||
width: 140,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: ElevatedButton.icon(
|
||||
icon: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/icons8-google-logo-48.png'),
|
||||
fit: BoxFit.contain,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'Google',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
onPressed: _googleLogin,
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'Google Login'
|
||||
/* Google 로그인 */,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
onPressed: _googleLogin,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
// (b) 구글 회원가입
|
||||
SizedBox(
|
||||
width: 300,
|
||||
child: ElevatedButton.icon(
|
||||
icon: Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: const BoxDecoration(
|
||||
image: DecorationImage(
|
||||
image: AssetImage('assets/images/icons8-google-logo-48.png'),
|
||||
fit: BoxFit.contain,
|
||||
// 애플 로그인 버튼 - 아이폰에서만 활성화
|
||||
if (Platform.isIOS)
|
||||
Container(
|
||||
width: 140,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5),
|
||||
child: ElevatedButton.icon(
|
||||
|
||||
icon: const Icon(
|
||||
Icons.apple,
|
||||
size: 24,
|
||||
color: Colors.black,
|
||||
),
|
||||
label: const Text(
|
||||
'Apple',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
onPressed: _appleLogin,
|
||||
),
|
||||
),
|
||||
),
|
||||
label: const Text(
|
||||
'Google Sign Up'
|
||||
/* Google 회원가입 */,
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.white,
|
||||
foregroundColor: Colors.black,
|
||||
side: const BorderSide(color: Colors.black),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 24),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
onPressed: _googleSignUp,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -11,6 +11,7 @@ import firebase_core
|
||||
import firebase_database
|
||||
import google_sign_in_ios
|
||||
import shared_preferences_foundation
|
||||
import sign_in_with_apple
|
||||
import webview_flutter_wkwebview
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
@ -20,5 +21,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
FLTFirebaseDatabasePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseDatabasePlugin"))
|
||||
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
SignInWithApplePlugin.register(with: registry.registrar(forPlugin: "SignInWithApplePlugin"))
|
||||
FLTWebViewFlutterPlugin.register(with: registry.registrar(forPlugin: "FLTWebViewFlutterPlugin"))
|
||||
}
|
||||
|
32
pubspec.lock
32
pubspec.lock
@ -464,6 +464,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -656,6 +664,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sign_in_with_apple:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: sign_in_with_apple
|
||||
sha256: "0975c23b9f8b30a80e27d5659a75993a093d4cb5f4eb7d23a9ccc586fea634e0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
sign_in_with_apple_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple_platform_interface
|
||||
sha256: c2ef2ce6273fce0c61acd7e9ff5be7181e33d7aa2b66508b39418b786cca2119
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
sign_in_with_apple_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sign_in_with_apple_web
|
||||
sha256: "44b66528f576e77847c14999d5e881e17e7223b7b0625a185417829e5306f47a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -23,6 +23,7 @@ dependencies:
|
||||
firebase_auth: ^4.15.3
|
||||
firebase_database: ^10.2.3
|
||||
google_sign_in: ^6.1.6
|
||||
sign_in_with_apple: ^5.0.0
|
||||
cupertino_icons: ^1.0.8
|
||||
fluttertoast: ^8.0.9
|
||||
flutter_launcher_icons: ^0.12.0
|
||||
|
Loading…
Reference in New Issue
Block a user