반응형
오늘의 목표
앞서 추가한 기본 화면에 구글 SignIn 버튼을 실제로 동작하도록 구현해보자.
#0. 구글 SignIn 관련 variable 설명
#1. 앱 시작시 build 전 initState
#2. 구글 SignIn 버튼 클릭시 동작
#0. 구글 SignIn 관련 variable 설명
- GoogleSignIn : 구글 로그인의 기능을 담당할 변수
- GoogleSignInAccount : 구글 로그인시 계정을 담을 변수
- userReference : 로그인시 user정보 db(firestore)에 저장할 때 사용할 변수
- isSignedIn : 앱 시작시 기 로그인여부를 확인할 변수
- DocumentSnapshot : Firestore에서 실시간 데이터를 주고받을 때 사용하는 용도
// variable for google sign in (very easy to use)
final GoogleSignIn googleSignIn = new GoogleSignIn();
// variable for firestore collection 'users'
final userReference = FirebaseFirestore.instance.collection('users');
...
bool isSignedIn = false;
...
final GoogleSignInAccount gCurrentUser = googleSignIn.currentUser;
...
DocumentSnapshot documentSnapshot = await userReference.doc(gCurrentUser.id).get();
#1. 앱 시작시 build 전 initState
- Firebase 앱 초기화
- 앱 실행시 기존에 로그인했었는지 체크
기존 로그인했었다면) 사용자 정보 저장
@override
void initState() {
super.initState();
Firebase.initializeApp().whenComplete(() {
print("completed");
setState(() {});
});
pageController = PageController();
// 앱 실행시 구글 사용자의 변경여부를 확인함
googleSignIn.onCurrentUserChanged.listen((gSignInAccount) {
controlSignIn(gSignInAccount); // 사용자가 있다면 로그인
}, onError: (gError) {
print("Error Message : " + gError);
});
googleSignIn.signInSilently();
// suppressErrors: false).then((gSignInAccount) {
// controlSignIn(gSignInAccount);
// }).catchError((gError) {
// print("Error Message : " + gError);
// });
}
// 로그인 상태 여부에 따라 isSignedIn flag값을 변경해줌
controlSignIn(GoogleSignInAccount signInAccount) async {
if(signInAccount != null) {
await saveUserInfoToFirestore();
setState(() {
isSignedIn = true;
});
} else {
setState(() {
isSignedIn = false;
});
}
}
saveUserInfoToFirestore() async {
// 현재 구글 로그인된 사용자 정보 가져오기
final GoogleSignInAccount gCurrentUser = googleSignIn.currentUser;
// 해당 유저의 db정보 가져오기
DocumentSnapshot documentSnapshot = await userReference.doc(gCurrentUser.id).get();
// 해당 유저의 db정보가 없다면
if(!documentSnapshot.exists) {
// 유저정보를 셋팅하는 페이지로 이동
final username = await Navigator.push(context, MaterialPageRoute(builder: (context) => CreateAccountPage()));
// 유저정보 셋팅된 값으로 db에 set
userReference.doc(gCurrentUser.id).set({
'id' : gCurrentUser.id,
'profileName' : gCurrentUser.displayName,
'username' : username,
'url' : gCurrentUser.photoUrl,
'email' : gCurrentUser.email,
'bio' : '',
'timestamp' : timestamp
});
// 해당 정보 다시 가져오기
documentSnapshot = await userReference.doc(gCurrentUser.id).get();
}
// 현재 유저정보에 값 셋팅하기
currentUser = User.fromDocument(documentSnapshot);
}
#2. 구글 SignIn 버튼 클릭시 동작
- 최초 SignIn 버튼 클릭을 하는 경우 구글 로그인 계정 추가 페이지가 뜬다.
GestureDetector(
onTap: loginUser,
child: Container(
width: 200,
height: 50,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/google_signin_button.png'),
fit: BoxFit.cover
)
),
),
)
- 계정을 선택하면 사용자 정보를 입력하는 Setting 창이 뜬다. (pop up)
- 유저네임을 입력하고 Submit 버튼 클릭시 유저 정보가 db에 저장되며 해당 유저정보를 가지고
홈 화면(feed page)으로 돌아온다.
전체 소스 (HomePage.dart)
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:instargram_clone_by_kyungsnim/models/user.dart';
import 'package:instargram_clone_by_kyungsnim/pages/TimeLinePage.dart';
import 'package:instargram_clone_by_kyungsnim/pages/SearchPage.dart';
import 'package:instargram_clone_by_kyungsnim/pages/ProfilePage.dart';
import 'package:instargram_clone_by_kyungsnim/pages/UploadPage.dart';
import 'package:instargram_clone_by_kyungsnim/pages/NotificationsPage.dart';
import 'package:instargram_clone_by_kyungsnim/pages/CreateAccountPage.dart';
// variable for google sign in (very easy to use)
final GoogleSignIn googleSignIn = new GoogleSignIn();
// variable for firestore collection 'users'
final userReference = FirebaseFirestore.instance.collection('users');
final DateTime timestamp = DateTime.now();
User currentUser;
class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool isSignedIn = false;
// 페이지 컨트롤
PageController pageController;
int getPageIndex = 0;
@override
void initState() {
super.initState();
Firebase.initializeApp().whenComplete(() {
print("completed");
setState(() {});
});
pageController = PageController();
// 앱 실행시 구글 사용자의 변경여부를 확인함
googleSignIn.onCurrentUserChanged.listen((gSignInAccount) {
controlSignIn(gSignInAccount); // 사용자가 있다면 로그인
}, onError: (gError) {
print("Error Message : " + gError);
});
googleSignIn.signInSilently();
// suppressErrors: false).then((gSignInAccount) {
// controlSignIn(gSignInAccount);
// }).catchError((gError) {
// print("Error Message : " + gError);
// });
}
// 로그인 상태 여부에 따라 isSignedIn flag값을 변경해줌
controlSignIn(GoogleSignInAccount signInAccount) async {
if(signInAccount != null) {
await saveUserInfoToFirestore();
setState(() {
isSignedIn = true;
});
} else {
setState(() {
isSignedIn = false;
});
}
}
saveUserInfoToFirestore() async {
// 현재 구글 로그인된 사용자 정보 가져오기
final GoogleSignInAccount gCurrentUser = googleSignIn.currentUser;
// 해당 유저의 db정보 가져오기
DocumentSnapshot documentSnapshot = await userReference.doc(gCurrentUser.id).get();
// 해당 유저의 db정보가 없다면
if(!documentSnapshot.exists) {
// 유저정보를 셋팅하는 페이지로 이동
final username = await Navigator.push(context, MaterialPageRoute(builder: (context) => CreateAccountPage()));
// 유저정보 셋팅된 값으로 db에 set
userReference.doc(gCurrentUser.id).set({
'id' : gCurrentUser.id,
'profileName' : gCurrentUser.displayName,
'username' : username,
'url' : gCurrentUser.photoUrl,
'email' : gCurrentUser.email,
'bio' : '',
'timestamp' : timestamp
});
// 해당 정보 다시 가져오기
documentSnapshot = await userReference.doc(gCurrentUser.id).get();
}
// 현재 유저정보에 값 셋팅하기
currentUser = User.fromDocument(documentSnapshot);
}
void dispose() {
pageController.dispose();
super.dispose();
}
loginUser() {
googleSignIn.signIn();
}
logoutUser() {
googleSignIn.signOut();
}
whenPageChanges(int pageIndex) {
setState(() {
this.getPageIndex = pageIndex;
});
}
onTapChangePage(int pageIndex) {
pageController.animateToPage(pageIndex, duration: Duration(milliseconds: 400), curve: Curves.bounceInOut);
}
buildHomeScreen() {
return Scaffold(
body: PageView(
children: <Widget>[
// 정상 로그인시 홈스크린 보인다.
TimeLinePage(), // 0번 pageIndex
SearchPage(), // 1번 pageIndex
UploadPage(), // 2번 pageIndex
NotificationsPage(), // 3번 pageIndex
ProfilePage(), // 4번 pageIndex
],
controller: pageController, // controller를 지정해주면 각 페이지별 인덱스로 컨트롤 가능
onPageChanged: whenPageChanges, // page가 바뀔때마다 whenPageChanges 함수가 호출되고 현재 pageIndex 업데이트해줌
physics: NeverScrollableScrollPhysics(),
),
bottomNavigationBar: CupertinoTabBar(
currentIndex: getPageIndex,
onTap: onTapChangePage,
activeColor: Colors.white,
inactiveColor: Colors.grey,
backgroundColor: Colors.black,
items: [
BottomNavigationBarItem(icon: Icon(Icons.home)),
BottomNavigationBarItem(icon: Icon(Icons.search)),
BottomNavigationBarItem(icon: Icon(Icons.photo_camera, size: 35)),
BottomNavigationBarItem(icon: Icon(Icons.notifications)),
BottomNavigationBarItem(icon: Icon(Icons.person)),
],
),
);
}
buildSignInScreen() {
return Scaffold(
body: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topRight,
end: Alignment.bottomLeft,
colors: [
Theme.of(context).accentColor,
Theme.of(context).primaryColor
]
)
),
alignment: Alignment.center,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Instargram',
style: TextStyle(
fontSize: 70,
color: Colors.white,
fontFamily: 'Signatra'
),
),
SizedBox(height: 200),
GestureDetector(
onTap: loginUser,
child: Container(
width: 200,
height: 50,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/google_signin_button.png'),
fit: BoxFit.cover
)
),
),
)
],
)
)
);
}
@override
Widget build(BuildContext context) {
if(isSignedIn) {
return buildHomeScreen();
} else {
return buildSignInScreen();
}
}
}
사용자 정보 추가 페이지 소스 (CreateAccountPage.dart)
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:instargram_clone_by_kyungsnim/widgets/HeaderWidget.dart';
class CreateAccountPage extends StatefulWidget {
@override
_CreateAccountPageState createState() => _CreateAccountPageState();
}
class _CreateAccountPageState extends State<CreateAccountPage> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
final _formKey = GlobalKey<FormState>();
String username;
TextEditingController usernameController;
submitUsername() {
final form = _formKey.currentState;
if(form.validate()) {
form.save();
SnackBar snackBar = SnackBar(content: Text('Welcome ' + username));
_scaffoldKey.currentState.showSnackBar(snackBar);
Timer(Duration(seconds: 4), () {
Navigator.pop(context, username);
});
}
}
@override
Widget build(BuildContext parentContext) {
return Scaffold(
key: _scaffoldKey,
appBar: header(context, title: 'Settings', disappearedBackButton: true),
body: ListView(
children: <Widget>[
Container(
child: Column(
children: <Widget>[
Padding(
padding: EdgeInsets.only(top: 25),
child: Center(
child: Text('Set up a username', style: TextStyle(fontSize: 25)),
)
),
Padding(
padding: EdgeInsets.all(16),
child: Container(
child: Form(
key: _formKey,
autovalidate: true,
child: TextFormField(
style: TextStyle(color: Colors.white,),
validator: (val) {
if(val.trim().length < 5 || val.isEmpty) {
return 'user name is too short (< 5)';
} else if (val.trim().length > 15 || val.isEmpty) {
return 'user name is too long (> 15)';
} else {
return null;
}
},
onSaved: (val) => username = val,
decoration: InputDecoration(
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.grey,),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white),
),
border: OutlineInputBorder(),
labelText: 'Username',
labelStyle: TextStyle(fontSize: 16),
hintText: 'must be at least 5 characters',
hintStyle: TextStyle(color: Colors.grey),
),
)
)
)
),
GestureDetector(
onTap: submitUsername,
child: Container(
height: 55,
width: 360,
decoration: BoxDecoration(
color: Colors.lightGreenAccent,
borderRadius: BorderRadius.circular(8),
),
child: Center(
child: Text(
'Submit',
style: TextStyle(
color: Colors.white,
fontSize: 16,
fontWeight: FontWeight.bold,
)
)
)
)
)
],
)
)
],
)
);
}
}
반응형
댓글