Flutter

Flutter 배우기 #3 Firebase 로그인 기능 구현

태은코딩 2025. 2. 9. 22:31

출처

https://www.youtube.com/watch?v=sXFjPktQt28

1. 프로젝트 준비

아래 코드를 안드로이드 스튜디오에 미리 작성

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: LoginPage(),
    );
  }
}

/// 로그인 페이지
class LoginPage extends StatefulWidget {
  const LoginPage({Key? key}) : super(key: key);

  @override
  State<LoginPage> createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  TextEditingController emailController = TextEditingController();
  TextEditingController passwordController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("로그인")),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            /// 현재 유저 로그인 상태
            Center(
              child: Text(
                "로그인해 주세요 🙂",
                style: TextStyle(
                  fontSize: 24,
                ),
              ),
            ),
            SizedBox(height: 32),

            /// 이메일
            TextField(
              controller: emailController,
              decoration: InputDecoration(hintText: "이메일"),
            ),

            /// 비밀번호
            TextField(
              controller: passwordController,
              obscureText: false, // 비밀번호 안보이게
              decoration: InputDecoration(hintText: "비밀번호"),
            ),
            SizedBox(height: 32),

            /// 로그인 버튼
            ElevatedButton(
              child: Text("로그인", style: TextStyle(fontSize: 21)),
              onPressed: () {
                // 로그인 성공시 HomePage로 이동
                Navigator.pushReplacement(
                  context,
                  MaterialPageRoute(builder: (_) => HomePage()),
                );
              },
            ),

            /// 회원가입 버튼
            ElevatedButton(
              child: Text("회원가입", style: TextStyle(fontSize: 21)),
              onPressed: () {
                // 회원가입
                print("sign up");
              },
            ),
          ],
        ),
      ),
    );
  }
}

/// 홈페이지
class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  TextEditingController jobController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("버킷 리스트"),
        actions: [
          TextButton(
            child: Text("로그아웃"),
            onPressed: () {
              print("sign out");
              // 로그인 페이지로 이동
              Navigator.pushReplacement(
                context,
                MaterialPageRoute(builder: (context) => LoginPage()),
              );
            },
          ),
        ],
      ),
      body: Column(
        children: [
          /// 입력창
          Padding(
            padding: const EdgeInsets.all(8),
            child: Row(
              children: [
                /// 텍스트 입력창
                Expanded(
                  child: TextField(
                    controller: jobController,
                    decoration: InputDecoration(
                      hintText: "하고 싶은 일을 입력해주세요.",
                    ),
                  ),
                ),

                /// 추가 버튼
                ElevatedButton(
                  child: Icon(Icons.add),
                  onPressed: () {
                    // create bucket
                    if (jobController.text.isNotEmpty) {
                      print("create bucket");
                    }
                  },
                ),
              ],
            ),
          ),
          Divider(height: 1),

          /// 버킷 리스트
          Expanded(
            child: ListView.builder(
              itemCount: 5,
              itemBuilder: (context, index) {
                String job = "$index";
                bool isDone = false;
                return ListTile(
                  title: Text(
                    job,
                    style: TextStyle(
                      fontSize: 24,
                      color: isDone ? Colors.grey : Colors.black,
                      decoration: isDone
                          ? TextDecoration.lineThrough
                          : TextDecoration.none,
                    ),
                  ),
                  // 삭제 아이콘 버튼
                  trailing: IconButton(
                    icon: Icon(CupertinoIcons.delete),
                    onPressed: () {
                      // 삭제 버튼 클릭시
                    },
                  ),
                  onTap: () {
                    // 아이템 클릭하여 isDone 업데이트
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

2. Firebase 사용준비

1. Firebase 이해하기 

 

2. Firebase 프로젝트 만들기

https://firebase.google.com/?gad_source=1&gclid=CjwKCAiA1eO7BhATEiwAm0Ee-Co1dmVgTOAzMJ4jVQRBfPMZUjAA-pEPrEWU-l59caMkSY5oXIbfPxoCAo8QAvD_BwE&gclsrc=aw.ds&hl=ko

 

Firebase | Google's Mobile and Web App Development Platform

개발자가 사용자가 좋아할 만한 앱과 게임을 빌드하도록 지원하는 Google의 모바일 및 웹 앱 개발 플랫폼인 Firebase에 대해 알아보세요.

firebase.google.com

위의 firebase사이트에 들어가서 

프로젝트 이름을 bucketlist로 설정 후 프로젝트 만든다.

3. Firebase Android 앱 만들기

여기서 안드로이드 버튼을 클릭한다.

안드로이드 스튜디오에서

app/ build.gradle의 코드에서 applicationID를 복사한다.

앱 등록을 하고 google-service.json을 다운받아서 안드로이드 스튜디오의 app부분으로 옮겨준다.

SDK추가하기 복사해서 settings.gradle에 붙혀넣는다.

여기서는 두번째 것을 복사해서

app의 build.gradle에 붙혀넣는다.

 

minSDK를 23으로 변경해주면 된다.

그리고 multiDenxEnabled true와

dependencies{ implementation "androidx.multidex:multidex:2.0.1" 추가한다. }

 

4. Firebase IOS 앱 추가하기

이건 맥 os 사용하는 기기만 가능해서 나는 패스했다.

5. 패키지 설치

flutter pub add firebase_core firebase_auth cloud_firestore provider

위 코드를 터미널에 입력해서 패키지를 설치한다.

이와 같이 설치된 모습을 볼 수 있다.

6. Firebase 사용 준비

이제 main.dart에 코드 붙여넣으면 사용 준비가 끝난다.

3. 로그인 기능 만들기

1. Firebase Auth를 이용하는 이유

2. Firebase Auth 사용 준비

https://console.firebase.google.com/u/0/?hl=ko

 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

콘솔에 접속

프로젝트 선택하고 빌드에서 Authentication 선택

로그인 방법에서 이메일을 선택

사용 설정 스위치 버튼을 on 상태로 변경하고 저장 버튼 누르기

이메일 비밀번호 로그인 사용 설정 완료

3. Auth service 만들기

1) lib 폴더를 우 클릭한 뒤 new file 만들어주기
2) auth_service.dart 파일 만들기

import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class AuthService extends ChangeNotifier {
  User? currentUser() {
// 현재 유저(로그인 되지 않은 경우 null 반환)
  }
  void signUp({
    required String email, // 이메일
    required String password, // 비밀번호
    required Function() onSuccess, // 가입 성공시 호출되는 함수
    required Function(String err) onError, // 에러 발생시 호출되는 함수
  }) async {
// 회원가입
  }
  void signIn({
    required String email, // 이메일
    required String password, // 비밀번호
    required Function() onSuccess, // 로그인 성공시 호출되는 함수
    required Function(String err) onError, // 에러 발생시 호출되는 함수
  }) async {
// 로그인
  }
  void signOut() async {
// 로그아웃
  }
}

다음과 같이 코드를 입력한다. 

 

구현할 함수 목록
- currentUser : 현재 유저 조회
(로그인을 하지 않은 경우 null을 반환)
- signUp: 회원가입
- signIn: 로그인
- signOut: 로그아웃

 

main.dart에서 8번째 줄 runApp을 지우고 아래 코드를 붙여 넣는다.

  runApp(
    MultiProvider(
      providers: [
        ChangeNotifierProvider(create: (context) =>
            AuthService()),
      ],
      child: const MyApp(),
    ),
  );

 

 

4. LoginPage에서 AuthService 접근하기

2) Consumer란?
Consumer의 주요 역할
1. 상태 변경 감지
- 특정 데이터가 변경되었을 때만 해당 UI 위젯을 다시 빌드
- 앱 전체를 리빌드하지 않고 필요한 부분만 업데이트
2. 재사용성
- 한 위젯 트리에서 상태를 읽고 UI를 업데이트 해야 할 때 사용

main.dart의 48번째 줄에서 Scafold에서 전구를 눌러 wrap Builder로 바꾼 뒤, 다음과 같이 변경해서 작성한다.

Consumer위에 , 컴마도 추가한다.

5. 회원가입 만들기

1) 로직 순서

1. 회원가입 버튼 클릭
2. 사용자가 입력한 이메일과 비밀번호 가져오기
3. AuthService에 SignUp 함수로 전달

다음과 같이 회원가입 부분에 코드를 작성

auth_service.dart의 회원가입 아래에 이메일 및 비밀번호 입력 여부 확인 부분 삽입

main.dart에 회원가입 성공과 에러발생코드 새로 삽입

 

try/on/catch 문
Try {
Firebase Auth 서버로 회원가입 요청
1. 회원 가입 성공
} on FirebaseAuthException catch(e){
2. 회원가입 실패
} catch(e) {
3. 서버와 통신 실패
}

try catch 문을 auth_service.dart파일의 중간 지점에 입력한다.

6. 로그인 만들기

로그인 버튼 부분에 위 코드 삽입

auth_service.dart의 로그인 부분에 코드 삽입

7. 유저 정보 가져오기

Auth_service.dart에 삽입

main.dart에 삽입

maindart에 로그입 성공 홈페이지 이동코드 삽입

8. 로그아웃 만들기

Auth_service.dart에 삽입

main.dart에 삽입

HomePage는 Consumer<AuthService>를 사용 중이지 않으므로
context.read문법을 사용해 AuthService에 접근

이제 테스트를 해보면 로그인 했을때 잘 들어가지고 로그아웃 했을때 문구가 나오는 것을 볼 수있다.