Don't think! Just do it!

종합 IT 기술 정체성 카오스 블로그! 이... 이곳은 어디지?

Flutter/Flutter Study

[Flutter] - Firebase auth! 이메일 로그인.

방피터 2022. 10. 11. 17:04

난 React native로 앱을 만들 때 firebase를 주로 사용했어. Firebase authenticate, Firebase firestore, Firebase Storage, Firebase function 이런 것들을 사용했었지. 사실 혼자서 백앤드까지 할 자신이 없어서 쉬워 보이는 거 찾다보니 google firebase랑 aws amplify 둘 중에 선택하게 되더라고 😅

둘 중에 뭐 할래?

난 결국 firebase를 선택했는데 amplify는 아직 본격적으로 사용해보지 않아서 잘 몰라. 게다가;;; flutter를 하고 있잖아? 그래서;;; 더더욱 amplify는 해볼 기회가 없어지네 ㅠㅠ 암튼 flutter나 firebase나 둘다 구글거니까 연동 잘되도록 만들어놨겠지? 일단 firebase에서 프로젝트를 하나 만들었어. 그리고 flutter 앱을 선택해보자구.

firebase new project

그러면 작업 단계가 나오는데 잘 따라해보자고!

flutter에 firebase추가

다 잘 따라하면 되는데 누락된 단계가 좀 있네;;

►firebase_core 설치해줘야 하고~👇

%flutter pub add firebase_core

그 다음 main.dart에서

► main함수를 async로 선언해야 해. 그래야 await를 사용하니깐

► await firebase.initializeApp 불러오기 전에 WidgetsFlutterBinding.ensureInitialized(); 한줄 더 추가해 줘야해. 아니면 시뻘건 에러를 볼 수 있음.

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  runApp(MaterialApp(
    title: 'Navigation',
    home: MyApp(),
  ));
}

추가 안해주면 아래와 같은 에러 발생!

암튼 잘 모르겠고! ㅋㅋ 서비스 바인딩에서 뭐 인스턴스 불러오려면 바인딩 초기화 되어 있어야 한데.

다 끝냈으면 디버그 일단 종료했다가 다시 시작해야해. 그러면 pod install 같은거 알아서 하거든. 다시 시작해서 앱이 잘 보이면 일단 연동은 끝~

자 이제 firebase authenticate 부터 시작해보자구. 우선 관련 패키지부터 설치 ㄱㄱ👇

%flutter pub add firebase_auth

그 다음에는 이메일과 비번으로 로그인할 수 있는 스크린을 하나 만들거야. 입력은 TextField를 사용했는데  이메일 validation 같은 것도 되는 TextFormField가 따로 있으니 각자 알아서 테스트 해보기~ 아래는 로그인 스크린 코드 👇👇👇

//ScreenLogin.dart
import 'dart:async';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';

class ScreenLogin extends StatefulWidget {
  const ScreenLogin({super.key});

  @override
  State<ScreenLogin> createState() => _ScreenLoginState();
}

class _ScreenLoginState extends State<ScreenLogin> {
  final emailContoller = TextEditingController();//email 입력 컨트롤러
  final passController = TextEditingController();//password 입력 컨트롤러
  //원래 이것들 dispose시켜줘야하는데 귀찮아서 안해줌. 니들은 하자~
  String errorString = '';//login error 보려고 만든 String state

  //firebase auth login 함수, 이멜 + 비번으로 로그인
  void fireAuthLogin() async {
    print('${emailContoller.text}, ${passController.text}');//괜히 출력해봄.
    await FirebaseAuth.instance
        .signInWithEmailAndPassword(
            email: emailContoller.text, password: passController.text)
        .catchError((error) => setState(() => errorString = error.message));
    print("${FirebaseAuth.instance.currentUser}");//괜히 출력해 봄.
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(children: [
          Container(
              child: Center(
            child: Image.asset("assets/images/peter.jpeg", width: 300),
          )),
          Container(
              padding: EdgeInsets.fromLTRB(0, 10, 0, 10),
              child: Text(
                "로그인하시던가",
                style: TextStyle(fontSize: 30, fontWeight: FontWeight.bold),
              )),
          Container(
              margin: const EdgeInsets.all(5),
              child: TextField(
                  controller: emailContoller,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(), label: Text("e-mail")))),
          Container(
              margin: const EdgeInsets.all(5),
              child: TextField(
                controller: passController,
                obscureText: true,//비번 *****<--- 으로 보이도록
                decoration: InputDecoration(
                    border: OutlineInputBorder(), label: Text("password")),
              )),
          Container(
              margin: EdgeInsets.fromLTRB(0, 0, 10, 0),
              child: Align(
                  alignment: Alignment.centerRight,
                  child: ElevatedButton(
                    onPressed: () => fireAuthLogin(),//로그인 버튼!
                    child: Text("로그인하라귯!"),
                  ))),
          Container(
              margin: EdgeInsets.all(10),
              child: Center(
                child: Text(errorString),
              ))
        ]),
      ),
    );
  }
}

나는 심심해서 image도 넣고 했는데 똑같이 하려면 프로젝트에 assets/images 폴더 만들고 사진 추가 그리고 pubspec.yaml에다가 폴더도 등록해야해. 귀찮으면 빼버리자 ㅋ

멋있는 나의 로그인 화면!

로그인 화면은 어찌저찌 만들었는데... 원래 이래야 하잖아? 로그인이 안되어 있으면 로그인 화면으로, 로그인이 되어 있으면 main 화면으로.. 이걸 한번 해보자구. 이 방법도 찾아보니 여러가지가 있었는데 StreamBuilder를 사용하는게 가장 깔끔한 방법같더라고. 이해도 쉽고!

Stream은 대상(패치한 데이터 같은 거)이 변경될 때마다 수행되는 애라고 보면 되는데 FirebaseAuth.instance.authStateChanges() <---얘가 Stream<User?> 반환함 ㅇㅇ 결국 이 Stream에 따라 로그인 화면으로 갈지 메인 화면으로 갈지 결정하는 MainPage 위젯을 새로 하나 만들어야 해. 그리고 main()에서 MainPage 위젯을 불러와야겠지.👇👇 주석에다가 코드 설명해놨으니까 참고하고

class MainPage extends StatelessWidget {
  const MainPage({super.key});
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: StreamBuilder<User?>(//이건 스트림빌더임 ㅇㅇ
      stream: FirebaseAuth.instance.authStateChanges(),//스트림에 authStateChanges를 등록함
      //auth 상태가 변경되면 -> user가 로그인을 하던지, 로그아웃을 하던지, 계정 삭제를 하던지
      builder: (context, snapshot) {//변경된 상태가 snapshot으로 빌드로 새로함.
        if (snapshot.hasData) {//snapshot이 데이터가 있으면
          return const MyApp();//기존의 MyApp() 위젯 빌드
        } else {
          return const ScreenLogin();//아니면 로그인해라~
        }
      },
    ));
  }
}

음.. 나머지는 테스트용 로그아웃 버튼, 그리고 로그인한 유저 정보 화면에 표시하는 정도? 만 더 추가해 줬어. 로그아웃은 FirebaseAuth.instance.SignOut()함수를 사용하면 되고 유저 정보는 FirebaseAuth.instance.currentUser 를 통해서 읽어올 수 있어.

//로그아웃
...
TextButton(
    onPressed: () => FirebaseAuth.instance.signOut(),
    child: Text("로그아웃")),
...             

//유저 정보 접근
...
Text("안녕! ${FirebaseAuth.instance.currentUser!.email}")
...

코드가 다 됐으면 firebase auth에 미리 이메일을 등록해두고 테스트를 해보자 👇👇👇

firebase auth login 잘 동작함. 🎉🎉

물론 이게 끝이 아니지. 계정을 새로 만드는 것도 해야하고 소셜 로그인도 해야 하는데... 음... 너무 길어지니까 다음 글에서 하자고... 너무 졸려! 안녕!

반응형