Search
🎋

Flutter Firebase auth 연동

생성일
2021/05/09 13:37
태그
Flutter
속성

Firebase Auth pub 설치

$ flutter pub add firebase_auth
Bash
복사

main.dart

import 'dart:io'; import 'package:chai_booster/Pages/LoginPage.dart'; import 'package:chai_booster/Pages/RootPage.dart'; import 'package:chai_booster/Providers/Auth/AuthProvider.dart'; import 'package:chai_booster/Routers.dart'; import 'package:firebase_auth/firebase_auth.dart'; import 'package:firebase_core/firebase_core.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; void main() { WidgetsFlutterBinding.ensureInitialized(); runApp(App()); } class App extends StatefulWidget { _AppState createState() => _AppState(); } class _AppState extends State<App> { // Set default `_initialized` and `_error` state to false bool _initialized = false; bool _error = false; // Define an async function to initialize FlutterFire void initializeFlutterFire() async { try { // Wait for Firebase to initialize and set `_initialized` state to true await Firebase.initializeApp(); setState(() { _initialized = true; }); } catch(e) { setState(() { _error = true; }); } } void initState() { initializeFlutterFire(); super.initState(); } Widget build(BuildContext context) { if(_error || !_initialized) { return MaterialApp( home: Scaffold( body: Center( child: Text('에러'), ), ) ); } return MaterialApp( home: MultiProvider( providers: [ ChangeNotifierProvider<AuthProvider>(create: (_) => AuthProvider()) ], child: RootPage(), ) ); } }
Dart
복사

AuthProvider.dart

import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; class AuthProvider with ChangeNotifier { FirebaseAuth _auth = FirebaseAuth.instance; User _user; AuthProvider() { _prepareUser(); } User getUser() { return _user; } void _prepareUser() { _user = _auth.currentUser; } void signInAnonymously() async { final authResult = await _auth.signInAnonymously(); setUser(authResult.user); } void setUser(User user) { _user = user; notifyListeners(); } void signOut() async { await _auth.signOut(); setUser(null); } void withdrawalAccount() async { await getUser().delete(); setUser(null); } }
Dart
복사

Home.dart

import 'package:chai_booster/Providers/Auth/AuthProvider.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; class HomePage extends StatefulWidget { HomePage({ Key key, this.title }) : super(key: key); final String title; _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { AuthProvider authProvider; Widget build(BuildContext context) { authProvider = Provider.of<AuthProvider>(context); return Scaffold( body: Center( child: SizedBox( child: ElevatedButton( onPressed: () => { authProvider.withdrawalAccount() }, child: Text('회원탈'), ), width: double.infinity, height: 44, ), ), ); } }
Dart
복사

LoginPage.dart

import 'package:chai_booster/Providers/Auth/AuthProvider.dart'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; class LoginPage extends StatefulWidget { LoginPage({ Key key, this.title }) : super(key: key); final String title; _LoginPageState createState() => _LoginPageState(); } class _LoginPageState extends State<LoginPage> { AuthProvider authProvider; void _asdf() { } Widget build(BuildContext context) { authProvider = Provider.of<AuthProvider>(context); return Scaffold( body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, mainAxisSize: MainAxisSize.max, children: <Widget>[ Text( 'ㅇㅕ기는 로그인 화면이 될거야', ), SizedBox(height: 80), Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: SizedBox( child: ElevatedButton( onPressed: () => { authProvider.signInAnonymously() }, child: Text('카카오톡 로그인'), ), width: double.infinity, height: 44, ) ), SizedBox(height: 12,) , Padding( padding: EdgeInsets.symmetric(horizontal: 20), child: SizedBox( child: ElevatedButton( onPressed: _asdf, child: Text('애플 로그인'), ), width: double.infinity, height: 44, ) ) ], ), ), ); } }
Dart
복사

Firebase Auth 추가 처리

Firebase Auth 를 이용해 인증을 처리하는 경우 인증 값은 FirebaseSDK에 의해 keychain에 저장됩니다. keychain의 생명주기는 앱과 다릅니다. 앱이 삭제되더라도 인증 값은 디바이스 내에 남아있고, 이 때문에 앱을 재설치 하더라도 이전 인증 값을 이용해 로그인을 시도하도 local 개발환경의 경우 로그인에 실패하게 됩니다.
Fiirebase Auth의 인증 키 생명주기는 사용자에게는 혼란을, 개발환경에서는 오류를 발생시킵니다.

업그레이드 된 AuthProvider.dart

import 'package:firebase_auth/firebase_auth.dart'; import 'package:flutter/foundation.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AuthProvider with ChangeNotifier { FirebaseAuth _auth = FirebaseAuth.instance; User _user; AuthProvider() { _prepareUser(); } User getUser() { return _user; } void _prepareUser() async { final pref = await SharedPreferences.getInstance(); bool isFirstLaunched = pref.getBool('isFirstLaunched') ?? false; if (!isFirstLaunched) { await FirebaseAuth.instance.signOut(); } await pref.setBool('isFirstLaunched', true); _user = _auth.currentUser; setUser(_auth.currentUser); } void signInAnonymously() async { final authResult = await _auth.signInAnonymously(); setUser(authResult.user); } void setUser(User user) { _user = user; notifyListeners(); } void signOut() async { await _auth.signOut(); setUser(null); } void withdrawalAccount() async { await getUser().delete(); setUser(null); } }
Dart
복사
앱의 첫 실행인지 여부 판단을 위해 Shared Preference 패키지를 사용합니다.
_prepareUser() 내부에서 앱의 첫 실행 판단을 통해서, 첫 실행의 경우 signOut() 함수를 호출함으로써 keychain 에 남아있던 인증 정보를 초기화 합니다.

Trouble Shooting

androd: com.google.firebase.FirebaseException: An internal error has occurred

iOS의 경우 firebase auth 를 flutter pub 으로 설치하면 cocoapod 까지 잘 연계되어 설치되었습니다. android의 경우 gradle 까지 작업은 되지 않아 gradle 파일을 직접 수정해줘야 합니다.
수정이 필요한 부분은 Firebase 버전마다 다를 수 있으므로 Firebase doc 을 링크합니다.