auth.dart 구현
0 - 기본적으로 앞의 글과 이어지며 firebase 이메일 인증과 구글 인증이 어플에 허용되어 있어야 합니다.
1. 목표
auth.dart을 구현함으로 firebase에서 지원하는 메일 인증과 구글 인증을 실행할 수 있도록 한다.
toast.dart을 구현해 오류를 출력한다.
2. 구현 내용
auth.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:graduationproject/methods/toast.dart';
import 'backPage.dart';
class AuthPage extends StatefulWidget {
AuthPage({Key key}) : super(key: key);
static const routeName = '/auth';
@override
_AuthPageState createState() => _AuthPageState();
}
firebase_auth을 임포트 해주고 오류를 출력하는 파일인 toast.dart을 임포트 해준다. 앞의 게시글에서 설명했듯이 routeName을 지정해 준다.
*이메일 인증을 구현하기 전에 먼저 어떻게 구현할 것지 설명하겠습니다.
사용자가 이메일을 올바르게 입력하고 인증을 원한다. -> 어플에서 firebase에게 인증을 요청한다. -> firebase가 해당 매일로 이메일을 보낸다. -> 어플에서 사용자에게 이메일 인증을 요청한다. -> 사용자가 이메일을 확인하고 링크를 누른다. -> 사용자가 어플에서 이메일 인증을 했음을 알려준다. -> 어플은 firebase에 사용자 인증을 허가한다.
좀 더 자세한 구현 내용은 Sign_up에서 설명하도록 하겠습니다.
String _message = '이메일이을 보냈습니다. 이메일을 인증해주세요.';
FirebaseUser _firebaseUser;
final _scaffoldKey = GlobalKey<ScaffoldState>();
@override
void initState() {
super.initState();
_streamOpen();
}
너무 쉬운 코드입니다. 클래스가 시작하면 가장 먼저 들리는 함수로 _streamOpen() 함수를 호출합니다.
당연히 FirebaseUser와 이메일 인증을 위한 GlobalKey을 선언해 준다.
_streamOpen() {
FirebaseAuth.instance.onAuthStateChanged.listen((user) {
if (user == null) {
Navigator.pushReplacementNamed(context, '/backPage');
return;
}
//이메일 확인을 누루지 않으면 Verified가 false이다.
setState(() => _firebaseUser = user);
if (!user.isEmailVerified) {
setState(() => _message = '이메일이을 보냈습니다. 이메일을 인증해주세요.');
return;
}
Navigator.pushReplacementNamed(context, '/backPage');
});
}
_streamOpen() 함수 코드입니다. onAuthStateChange 메서드는 Listener가 등록돼 직후, user가 로그인한 경우, 현재 user가 로그 아웃한 경우, 현재 user가 변경될 때 UI 스레드에서 호출됩니다. user가 null일 때 backPage로 이동한다.(backPage는 원래 main.dart에 있던 코드이다. 사용자가 기본적으로 보는 UI라고 생각하면 된다.) 이렇게 구현한 이유는 Splash.dart(앞의 글 참고)가 끝나면 auth.dart 페이지로 넘어가는데 auth에서 사용자 인증이 되어 있지 않으면 로그인 회원가입이 가능한 backPage로 이동할 수 있도록 한 것이다. 만약 user가 null이 아니라면 인증이 되어 있는 것임으로 메일 인증이 되었는지 확인해야 한다. 이메일 인증이 되어 있지 않다면 user.isEmailVerified 가 false가 될 것이다. 이때 이메일을 인증해 달라는 메시지를 보낸다. 만약 모든 인증이 완료되었다면 backPage로 이동한다.(사실 backPage을 이동할 때 사용자의 정보를 넘겨줘서 backPage에서 권한을 주는 느낌으로 로그인 안 했을 때와 다른 화면을 보여주어야 하는데 그건 page로 arguments을 넘기는 코드를 구현할 때 하도록 하자)
@override
Widget build(BuildContext context) {
if(app.loading) return _loading();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('이메일 확인')
),
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(height: 50,),
Text(
_message,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
fontFamily: 'Nanum Barumpen',
),
),
SizedBox(height: 20,),
_firebaseUser == null ? CircularProgressIndicator() : _buildReloadButton()
],
),
),
),
);
}
auth.dart도 구현된 페이지 화면이 있다. 이메일을 인증하라고 요청과 , 인증을 확인하는 페이지인데 위젯으로 구현되어 있으며 _firebaseUser 가 null 일 때와 아닐 때 을 구현했는데 _firebaseUser가 null이 면 CircularProgressIndicator() 함수가 실행된다. 이 함수는 flutter에서 지원하는 로딩 중 함수 인다. 이 코드만 보면 사용자 인증이 안되면 계속 로딩 중이 되는 거 아닌가라는 의문이 생길 수 있는데 initState() 함수에 있는 _streamOpen() 함수를 생각해 보면 그렇지 않다. 애초에 user가 null이면 backPage 화면으로 넘어간다. 그럼 왜 CiircularProgressIndicator() 함수를 정의했는가? 사용자 인증은 했는데 아직 서버 데이터가 완전히 이동하지 않았을 때 즉 진짜 로딩 중일 때 로딩 중 함수가 호출되는 것이다. 위젯 빌드가 보이는 순간 이미 사용자는 인증을 하고 있는 상태이다. null이 false 일 때는 _buildReloadButton() 함수를 실행한다.
Widget _buildReloadButton() {
return RaisedButton(
child: Text(
'이메일을 확인했으면 클릭하세요',
style: TextStyle(
fontSize: 20,
),
),
onPressed: () async {
final user = await FirebaseAuth.instance.currentUser();
await user.reload();
if (!user.isEmailVerified) {
toastError(_scaffoldKey, '이메일이 인증되지 않았습니다 다시 시도해주세요');
return;
}
Navigator.pushReplacementNamed(context, '/backPage');
},
);
}
이 버튼 함수는 이메일 인증 함수로 버튼을 누르면 현재 사용자를 불러옵니다. user.isEmailVerified 가 false면 toastError class을 호출해 오류를 출력합니다. null이 아니면 역시 backPage로 이동합니다.
3. 전제 소스 코드 - auth.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:graduationproject/methods/toast.dart';
import 'backPage.dart';
class AuthPage extends StatefulWidget {
AuthPage({Key key}) : super(key: key);
static const routeName = '/auth';
@override
_AuthPageState createState() => _AuthPageState();
}
class AppState{
bool loading;
AppState(this.loading);
}
class _AuthPageState extends State<AuthPage> {
//StreamSubscription<FirebaseUser> _subscriptionAuth;
String _message = '이메일이을 보냈습니다. 이메일을 인증해주세요.';
FirebaseUser _firebaseUser;
final _scaffoldKey = GlobalKey<ScaffoldState>();
final app = AppState(false);
@override
void initState() {
super.initState();
_streamOpen();
}
// @override
// void dispose() {
// //_subscriptionAuth.cancel();
// super.dispose();
// }
//로그인 인증 stream
_streamOpen() {
FirebaseAuth.instance.onAuthStateChanged.listen((user) {
if (user == null) {
Navigator.pushReplacementNamed(context, '/backPage', arguments: backPage(user: null));
return;
}
//이메일 확인을 누루지 않으면 Verified가 false이다.
setState(() => _firebaseUser = user);//?? 이코드의 의미가 무엇인가? setState을 왜 해주었는가?
if (!user.isEmailVerified) {
setState(() => _message = '이메일이을 보냈습니다. 이메일을 인증해주세요.');
return;
}
Navigator.pushReplacementNamed(context, '/backPage', arguments: backPage(user : _firebaseUser));
});
}
Widget _buildReloadButton() {
return RaisedButton(
child: Text(
'이메일을 확인했으면 클릭하세요',
style: TextStyle(
fontSize: 20,
),
),
onPressed: () async {
final user = await FirebaseAuth.instance.currentUser();
await user.reload();
if (!user.isEmailVerified) {
toastError(_scaffoldKey, '이메일이 인증되지 않았습니다 다시 시도해주세요');
return;
}
Navigator.pushReplacementNamed(context, '/backPage', arguments: backPage(user : user));
},
);
}
@override
Widget build(BuildContext context) {
if(app.loading) return _loading();
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(
title: Text('이메일 확인')
),
body: Container(
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(height: 50,),
Text(
_message,
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 30,
fontFamily: 'Nanum Barumpen',
),
),
SizedBox(height: 20,),
_firebaseUser == null ? CircularProgressIndicator() : _buildReloadButton()
],
),
),
),
);
}
//로딩중을 보여주는 위젯
Widget _loading() {
return Scaffold(
appBar: AppBar(title: Text("loading...")),
body: Center(child: CircularProgressIndicator())
);
}
}
다음 코드는 페이지가 이동할 때 데이터가 함께 이동하는 코드까지 포함되어 있습니다. 로딩 함수 설명 등 앞에 게시물과 겹치는 설명은 생략했습니다. toast.dart 또한 설명을 생략했습니다.
4. 구현된 화면
5. 앞으로 구현할 내용
Sign_up과 Sign_in을 구현한다.
출처 :
Flutter Documentation
The landing page for Flutter documentation.
flutter.dev
memi’s startup
Development logs
fkkmemi.github.io
'전공_STUDY > Flutter 어플 개발' 카테고리의 다른 글
flutter로 중고장터 만들기 Sign_in_up.dart(firebase, provider 사용) (0) | 2020.06.27 |
---|---|
flutter로 중고장터 만들기 main.dart 수정(route 사용) (0) | 2020.06.26 |
Splash.dart 구현과 main.dart의 코드 정리 (1) | 2020.05.29 |
flutter firebase을 이용한 구글 인증으로 회원관리 (0) | 2020.05.15 |
Flutter 어플 개발 Sign_up.dart(firebase 연동) (2) | 2020.04.28 |