FirebaseUI를 사용하여 Flutter 앱에 사용자 인증 흐름 추가

1. 시작하기 전에

이 Codelab에서는 FlutterFire UI 패키지를 사용하여 Flutter 앱에 Firebase 인증을 추가하는 방법을 알아봅니다. 이 패키지를 사용하면 이메일/비밀번호 인증과 Google 로그인 인증을 Flutter 앱에 추가합니다. 또한 Firebase 프로젝트를 설정하고 FlutterFire CLI를 사용하여 Flutter 앱에서 Firebase를 초기화하는 방법을 알아봅니다.

기본 요건

이 Codelab에서는 Flutter를 사용한 경험이 있다고 가정합니다. 그렇지 않은 경우 먼저 기본사항을 학습하는 것이 좋습니다. 다음 링크가 도움이 됩니다.

Firebase를 사용해 본 경험도 있지만 Flutter 프로젝트에 Firebase를 추가해 본 적이 없어도 괜찮습니다. Firebase Console에 익숙하지 않거나 Firebase를 처음 사용하는 경우 먼저 다음 링크를 참조하세요.

생성할 항목

이 Codelab에서는 인증에 Firebase를 사용하여 Flutter 앱의 인증 흐름을 빌드하는 방법을 설명합니다. 애플리케이션에는 로그인 화면, '등록' 화면, 비밀번호 복구 화면, 사용자 프로필 화면이 있습니다.

6604fc9157f2c6ae.png eab9509a41074930.png da49189a5838e0bb.png B2ccfb3632b77878.png

학습할 내용

이 Codelab에서 다루는 내용은 다음과 같습니다.

  • Flutter 앱에 Firebase 추가
  • Firebase Console 설정
  • Firebase CLI를 사용하여 애플리케이션에 Firebase 추가
  • FlutterFire CLI를 사용하여 Dart에서 Firebase 구성 생성
  • Flutter 앱에 Firebase 인증 추가
  • Console에서 Firebase 인증 설정
  • firebase_ui_auth 패키지로 이메일 및 비밀번호 로그인 추가
  • firebase_ui_auth 패키지로 사용자 등록 추가
  • '비밀번호 찾기' 페이지 추가
  • firebase_ui_auth(으)로 Google 로그인 추가 중
  • 멀티 로그인 제공업체를 사용할 수 있도록 앱 구성
  • firebase_ui_auth 패키지를 사용하여 애플리케이션에 사용자 프로필 화면 추가

이 Codelab에서는 특히 firebase_ui_auth 패키지를 사용하여 강력한 인증 시스템을 추가하는 것과 관련이 있습니다. 보시다시피 위의 모든 기능을 포함하는 이 앱 전체는 약 100줄의 코드로 구현할 수 있습니다.

필요한 사항

  • Flutter에 관한 실무 지식 및 설치된 SDK
  • 텍스트 편집기 (JetBrains IDE, Android 스튜디오, VS Code는 Flutter에서 지원됨)
  • Chrome 브라우저 또는 Flutter용으로 선호하는 다른 개발 타겟. (이 Codelab의 일부 터미널 명령어는 Chrome에서 앱을 실행한다고 가정합니다.)

2. Firebase 프로젝트 만들기 및 설정

가장 먼저 완료해야 하는 작업은 Firebase 웹 콘솔에서 Firebase 프로젝트를 만드는 것입니다.

Firebase 프로젝트 만들기

  1. Firebase에 로그인합니다.
  2. Firebase Console에서 프로젝트 추가 (또는 프로젝트 만들기)를 클릭하고 Firebase 프로젝트의 이름 (예: 'FlutterFire-UI-Codelab')을 입력합니다.

df42a5e3d9584b48.png

  1. 프로젝트 만들기 옵션을 클릭합니다. 메시지가 표시되면 Firebase 약관에 동의합니다. 이 앱에는 애널리틱스를 사용하지 않으므로 Google 애널리틱스 설정을 건너뛰세요.

d1fcec48bf251eaa.png

Firebase 프로젝트에 대한 자세한 내용은 Firebase 프로젝트 이해를 참조하세요.

빌드 중인 앱은 Firebase 인증을 사용하여 사용자가 앱에 로그인할 수 있도록 허용합니다. 또한 신규 사용자를 Flutter 애플리케이션에서 등록할 수도 있습니다.

Firebase 인증은 Firebase Console을 통해 사용 설정해야 하며 사용 설정한 후에는 특별한 구성이 필요합니다.

Firebase 인증에 이메일 로그인 사용 설정

사용자가 웹 앱에 로그인하도록 허용하려면 먼저 이메일 및 비밀번호 로그인 방법을 사용합니다. 나중에 Google 로그인 방법을 추가합니다.

  1. Firebase Console에서 왼쪽 패널의 빌드 메뉴를 펼칩니다.
  2. 인증을 클릭한 후 시작하기 버튼을 클릭하고 로그인 방법 탭을 클릭합니다 (또는 여기를 클릭하여 로그인 방법 탭으로 바로 이동).
  3. 로그인 제공업체 목록에서 이메일/비밀번호를 클릭하고 사용 설정 스위치를 사용으로 설정한 후 저장을 클릭합니다. 58e3e3e23c2f16a4.png

3. Flutter 앱 설정

시작하기 전에 시작 코드를 다운로드하고 Firebase CLI를 설치해야 합니다.

시작 코드 가져오기

명령줄에서 GitHub 저장소를 클론합니다.

git clone https://github.com/flutter/codelabs.git flutter-codelabs

또는 GitHub 의 CLI 도구가 설치된 경우 다음 안내를 따릅니다.

gh repo clone flutter/codelabs flutter-codelabs

샘플 코드는 Codelab 컬렉션 코드가 포함된 flutter-codelabs 디렉터리에 클론해야 합니다. 이 Codelab의 코드는 flutter-codelabs/firebase-auth-flutterfire-ui 하위 디렉터리에 있습니다.

flutter-codelabs/firebase-auth-flutterfire-ui 디렉터리에는 두 개의 Flutter 프로젝트가 있습니다. 하나는 complete이고 다른 하나는 start입니다. start 디렉터리에는 불완전한 프로젝트가 포함되어 있으며 여기서 가장 많은 시간을 보냅니다.

cd flutter-codelabs/firebase-auth-flutterfire-ui/start

앞으로 건너뛰거나 완료되었을 때 어떻게 표시될지 확인하려면 완료라는 이름의 디렉터리를 확인하여 상호 참조합니다.

Codelab을 따라 하면서 코드를 직접 추가하려면 flutter-codelabs/firebase-auth-flutterfire-ui/start에서 Flutter 앱으로 시작하고 Codelab 전반에 걸쳐 이 프로젝트에 코드를 추가해야 합니다. 해당 디렉터리를 열거나 원하는 IDE로 가져옵니다.

Firebase CLI 설치

Firebase CLI는 Firebase 프로젝트를 관리하는 도구를 제공합니다. 잠시 후에 설치하게 될 FlutterFire CLI에는 CLI가 필요합니다.

CLI를 설치하는 방법에는 여러 가지가 있습니다. MacOS 또는 Linux를 사용하는 경우 가장 간단한 방법은 터미널에서 다음 명령어를 실행하는 것입니다.

curl -sL https://firebase.tools | bash

CLI를 설치한 후에는 Firebase에 인증해야 합니다.

  1. 다음 명령어를 실행하여 Google 계정으로 Firebase에 로그인합니다.
firebase login
  1. 이 명령어는 로컬 머신을 Firebase에 연결하고 Firebase 프로젝트에 대한 액세스 권한을 부여합니다.
  1. Firebase 프로젝트를 나열하여 CLI가 올바르게 설치되었고 계정에 액세스할 수 있는지 테스트합니다. 다음 명령어를 실행합니다.
firebase projects:list
  1. 표시된 목록은 Firebase Console에 나열된 Firebase 프로젝트와 같아야 합니다. flutterfire-ui-codelab.개 이상 표시되어야 합니다.

FlutterFire CLI 설치

FlutterFire CLI는 Flutter 앱에서 지원되는 모든 플랫폼에서 Firebase 설치 프로세스를 쉽게 만들어 주는 도구입니다. Firebase CLI를 기반으로 빌드되었습니다.

먼저 CLI를 설치합니다.

dart pub global activate flutterfire_cli

CLI가 설치되었는지 확인합니다. 다음 명령어를 실행하고 CLI가 도움말 메뉴를 출력하는지 확인합니다.

flutterfire -—help

Flutter 앱에 Firebase 프로젝트 추가

FlutterFire 구성

FlutterFire를 사용하여 Flutter 앱에서 Firebase를 사용하는 데 필요한 Dart 코드를 생성할 수 있습니다.

flutterfire configure

이 명령어를 실행하면 사용할 Firebase 프로젝트와 설정할 플랫폼을 선택하라는 메시지가 표시됩니다.

다음 스크린샷은 응답해야 하는 프롬프트를 보여줍니다.

  1. 사용할 프로젝트를 선택합니다. 이 경우에는 flutterfire-ui-codelab 1359cdeb83204baa.png를 사용합니다.
  2. 사용할 플랫폼을 선택합니다. 이 Codelab에는 웹, iOS, Android용 Flutter용 Firebase 인증을 구성하는 단계가 있지만 모든 옵션을 사용하도록 Firebase 프로젝트를 설정할 수도 있습니다. 301c9534f594f472.png
  3. 이 스크린샷은 프로세스 마지막의 출력을 보여줍니다. Firebase에 익숙하다면 콘솔에서 플랫폼 애플리케이션 (예: Android 애플리케이션)을 만들지 않아도 되며 FlutterFire CLI가 알아서 생성했음을 알 수 있습니다. 12199a85ade30459.png

이 작업이 완료되면 텍스트 편집기에서 Flutter 앱을 확인합니다. FlutterFire CLI에서 firebase_options.dart라는 새 파일을 생성했습니다. 이 파일에는 각 플랫폼에 필요한 Firebase 구성을 보유하는 정적 변수가 있는 FirebaseOptions라는 클래스가 포함되어 있습니다. flutterfire configure를 실행할 때 모든 플랫폼을 선택한 경우 web, android, ios, macos라는 정적 값이 표시됩니다.

firebase_options.dart

import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
import 'package:flutter/foundation.dart'
   show defaultTargetPlatform, kIsWeb, TargetPlatform;

/// Default [FirebaseOptions] for use with your Firebase apps.
///
/// Example:
/// ```dart
/// import 'firebase_options.dart';
/// // ...
/// await Firebase.initializeApp(
///   options: DefaultFirebaseOptions.currentPlatform,
/// );
/// ```
class DefaultFirebaseOptions {
 static FirebaseOptions get currentPlatform {
   if (kIsWeb) {
     return web;
   }
   // ignore: missing_enum_constant_in_switch
   switch (defaultTargetPlatform) {
     case TargetPlatform.android:
       return android;
     case TargetPlatform.iOS:
       return ios;
     case TargetPlatform.macOS:
       return macos;
   }

   throw UnsupportedError(
     'DefaultFirebaseOptions are not supported for this platform.',
   );
 }

 static const FirebaseOptions web = FirebaseOptions(
   apiKey: 'AIzaSyCqFjCV_9CZmYeIvcK9FVy4drmKUlSaIWY',
   appId: '1:963656261848:web:7219f7fca5fc70afb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   authDomain: 'flutterfire-ui-codelab.firebaseapp.com',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   measurementId: 'G-DGF0CP099H',
 );

 static const FirebaseOptions android = FirebaseOptions(
   apiKey: 'AIzaSyDconZaCQpkxIJ5KQBF-3tEU0rxYsLkIe8',
   appId: '1:963656261848:android:c939ccc86ab2dcdbb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
 );

 static const FirebaseOptions ios = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );

 static const FirebaseOptions macos = FirebaseOptions(
   apiKey: 'AIzaSyBqLWsqFjYAdGyihKTahMRDQMo0N6NVjAs',
   appId: '1:963656261848:ios:d9e01cfe8b675dfcb237ad',
   messagingSenderId: '963656261848',
   projectId: 'flutterfire-ui-codelab',
   storageBucket: 'flutterfire-ui-codelab.appspot.com',
   iosClientId: '963656261848-v7r3vq1v6haupv0l1mdrmsf56ktnua60.apps.googleusercontent.com',
   iosBundleId: 'com.example.complete',
 );
}

Firebase는 Firebase 프로젝트의 특정 플랫폼을 위한 특정 빌드를 지칭할 때 애플리케이션이라는 단어를 사용합니다. 예를 들어 FlutterFire-ui-codelab이라는 Firebase 프로젝트에는 Android용, iOS용, MacOS용, 웹용 등 여러 애플리케이션이 있습니다.

DefaultFirebaseOptions.currentPlatform 메서드는 Flutter에서 노출된 TargetPlatform enum을 사용하여 앱이 실행 중인 플랫폼을 감지한 다음 올바른 Firebase 애플리케이션에 필요한 Firebase 구성 값을 반환합니다.

Flutter 앱에 Firebase 패키지 추가

마지막 설정 단계는 관련 Firebase 패키지를 Flutter 프로젝트에 추가하는 것입니다. firebase_options.dart 파일은 아직 추가되지 않은 Firebase 패키지를 사용하므로 오류가 있습니다. 터미널에서 flutter-codelabs/firebase-emulator-suite/start에 있는 Flutter 프로젝트의 루트에 있는지 확인합니다. 그런 후 다음 세 가지 명령어를 실행합니다.

flutter pub add firebase_core
flutter pub add firebase_auth
flutter pub add firebase_ui_auth

현 시점에서는 이러한 패키지만 사용하면 됩니다.

Firebase 초기화

추가된 패키지를 사용하고 DefaultFirebaseOptions.currentPlatform,main.dart 파일의 main 함수에서 코드를 업데이트합니다.

main.dart

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


 runApp(const MyApp());
}

이 코드는 두 가지 역할을 합니다.

  1. WidgetsFlutterBinding.ensureInitialized()는 Flutter 프레임워크가 완전히 부팅될 때까지 애플리케이션 위젯 코드 실행을 시작하지 않도록 Flutter에 지시합니다. Firebase는 프레임워크를 실행해야 하는 기본 플랫폼 채널을 사용합니다.
  2. Firebase.initializeApp는 Flutter 앱과 Firebase 프로젝트 간의 연결을 설정합니다. DefaultFirebaseOptions.currentPlatform는 생성된 firebase_options.dart 파일에서 가져옵니다. 이 정적 값은 실행 중인 플랫폼을 감지하고 해당하는 Firebase 키를 전달합니다.

4. 초기 Firebase UI 인증 페이지 추가

인증용 Firebase UI는 애플리케이션의 전체 화면을 나타내는 위젯을 제공합니다. 이러한 화면은 로그인, 등록, 비밀번호 찾기, 사용자 프로필 등 애플리케이션 전반의 다양한 인증 흐름을 처리합니다. 시작하려면 앱에 기본 애플리케이션의 인증 가이드 역할을 하는 방문 페이지를 추가합니다.

Material 또는 Cupertino 앱

FlutterFire UI를 사용하려면 애플리케이션이 MaterialApp 또는 CupertinoApp으로 래핑되어야 합니다. 선택한 항목에 따라 UI가 Material 또는 Cupertino 위젯의 차이점을 자동으로 반영합니다. 이 Codelab에서는 app.dart의 앱에 이미 추가된 MaterialApp를 사용합니다.

app.dart

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

class MyApp extends StatelessWidget {
 const MyApp({super.key});
 @override
 Widget build(BuildContext context) {
   return MaterialApp(
     theme: ThemeData(
       primarySwatch: Colors.blue,
     ),
     home: const AuthGate(),
   );
 }
}

인증 상태 확인

로그인 화면을 표시하려면 사용자가 현재 인증되었는지 확인해야 합니다. 이를 확인하는 가장 일반적인 방법은 Firebase 인증 플러그인을 사용하여 FirebaseAuth의 authStateChanges를 수신 대기하는 것입니다.

위의 코드 샘플에서 MaterialApp는 빌드 메서드에서 AuthGate 위젯을 빌드하고 있습니다. (FlutterFire UI에서 제공하지 않는 맞춤 위젯입니다.)

authStateChanges 스트림을 포함하도록 위젯을 업데이트해야 합니다.

authStateChanges API는 현재 사용자 (로그인한 경우)와 함께 Stream를 반환하고 그렇지 않은 경우 null을 반환합니다. 애플리케이션에서 이 상태를 구독하려면 Flutter의 StreamBuilder 위젯을 사용하고 여기에 스트림을 전달하면 됩니다.

StreamBuilder는 개발자가 전달한 Stream에서 가져온 데이터의 최신 스냅샷을 기반으로 자체 빌드하는 위젯입니다. 스트림에서 새 스냅샷을 내보내면 자동으로 다시 빌드됩니다.

auth_gate.dart의 코드를 업데이트합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [],
          );
        }

        return const HomeScreen();
      },
    );
  }
}
  • StreamBuilder.stream에는 앞서 언급한 스트림인 FirebaseAuth.instance.authStateChanged가 전달되고 있으며, 이 스트림은 사용자가 인증한 경우 Firebase User 객체를 반환합니다. (그렇지 않으면 null이 반환됩니다.)
  • 다음으로, 코드는 snapshot.hasData를 사용하여 스트림의 값에 User 객체가 포함되어 있는지 확인합니다.
  • 없는 경우에는 SignInScreen 위젯을 반환합니다. 현재 이 화면에서는 아무 작업도 하지 않습니다. 다음 단계에서 업데이트됩니다.
  • 그 외의 경우에는 인증된 사용자만 액세스할 수 있는 애플리케이션의 주요 부분인 HomeScreen를 반환합니다.

SignInScreen는 FlutterFire UI 패키지에서 제공하는 위젯입니다. 이는 이 Codelab의 다음 단계에서 중점적으로 다룹니다. 이 시점에서 앱을 실행하면 빈 로그인 화면이 표시됩니다.

5. 로그인 화면

FlutterFire UI에서 제공하는 SignInScreen 위젯은 다음 기능을 추가합니다.

  • 사용자가 로그인하도록 허용
  • 사용자가 비밀번호를 잊어버린 경우 '비밀번호 찾기'를 탭하면 비밀번호 재설정 양식이 표시됩니다.
  • 아직 등록되지 않은 사용자는 '등록'을 탭하면 가입할 수 있는 다른 양식으로 이동합니다.

이번에도 코드 몇 줄이면 됩니다. AuthGate 위젯에서 코드를 다시 호출합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
  const AuthGate({super.key});

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<User?>(
      stream: FirebaseAuth.instance.authStateChanges(),
      builder: (context, snapshot) {
        if (!snapshot.hasData) {
          return SignInScreen(
            providers: [
              EmailAuthProvider(), // new
            ],
          );
        }

        return const HomeScreen();
      },
    );
  }
}

SignInScreen 위젯과 그 providers 인수는 앞서 언급한 모든 기능을 가져오는 데 필요한 유일한 코드입니다. 이제 '이메일'과 '비밀번호' 텍스트 입력과 '로그인' 버튼이 있는 로그인 화면이 표시됩니다.

기능적이기는 하지만 스타일이 없습니다. 위젯은 로그인 화면의 모양을 맞춤설정하는 매개변수를 노출합니다. 예를 들어 회사 로고를 추가할 수 있습니다.

로그인 화면 맞춤설정

headerBuilder

SignInScreen.headerBuilder 인수를 사용하면 로그인 양식 위에 원하는 위젯을 추가할 수 있습니다. 다음 코드를 사용하여 auth_gate.dart 파일을 업데이트합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('assets/flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

headerBuilder 인수에는 FlutterFire UI 패키지에 정의되어 있는 HeaderBuilder 유형의 함수가 필요합니다.

typedef HeaderBuilder = Widget Function(
 BuildContext context,
 BoxConstraints constraints,
 double shrinkOffset,
);

콜백이므로 BuildContextBoxConstraints와 같이 사용할 수 있는 값을 노출하고 위젯을 반환해야 합니다. 반환하는 위젯이 화면 상단에 표시됩니다. 이 예에서 새 코드는 화면 상단에 이미지를 추가합니다. 이제 애플리케이션이 다음과 같이 표시됩니다.

73d7548d91bbd2ab.png

자막 작성 도구

로그인 화면에는 화면을 맞춤설정할 수 있는 세 가지 추가 매개변수 subtitleBuilder, footerBuilder, sideBuilder가 표시됩니다.

subtitleBuilder는 콜백 인수가 AuthAction 유형인 작업을 포함한다는 점에서 약간 다릅니다. AuthAction는 사용자가 있는 화면이 '로그인' 화면인지 '등록' 화면인지 감지하는 데 사용할 수 있는 enum입니다.

subtitleBuilder를 사용하도록 auth_gate.dart의 코드를 업데이트합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
              EmailAuthProvider()
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

애플리케이션을 새로고침하면 다음과 같이 표시됩니다.

footerBuilder 인수는 subtitleBuilder와 동일합니다. 이미지가 아닌 텍스트용이므로 BoxConstraints 또는 shrinkOffset를 노출하지 않습니다. 원하는 위젯은 모두 추가할 수 있습니다.

이 코드를 사용하여 로그인 화면에 바닥글을 추가합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider()
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }}

사이드 빌더

SignInScreen.sidebuilder 인수가 콜백을 수락하며 이번에는 해당 콜백의 인수는 BuildContextdouble shrinkOffset입니다. sideBuilder가 반환하는 위젯은 로그인 양식 왼쪽에 와이드 스크린에만 표시됩니다. 실제로 위젯이 데스크톱 및 웹 앱에만 표시됩니다.

내부적으로 FlutterFire UI는 중단점을 사용하여 헤더 콘텐츠를 모바일과 같이 큰 화면에 표시해야 하는지 아니면 측면 콘텐츠를 표시해야 하는지(와이드 화면, 데스크톱 또는 웹) 결정합니다. 특히 화면 너비가 800픽셀을 초과하는 경우 사이드 빌더 콘텐츠가 표시되고 헤더 콘텐츠는 표시되지 않습니다. 화면 너비가 800픽셀 미만인 경우 그 반대입니다.

auth_gate.dart의 코드를 업데이트하여 sideBuilder 위젯을 추가합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }
       return const HomeScreen();
     },
   );
 }
}

이제 (Flutter 웹 또는 MacOS를 사용하는 경우) 창의 너비를 확장할 때 앱이 다음과 같이 표시됩니다.

8dc60b4e5d7dd2d0.png

사용자 생성

이제 이 화면의 모든 코드가 완료되었습니다. 로그인하려면 사용자를 만들어야 합니다. '등록' 화면에서 등록하거나 Firebase Console에서 사용자를 만들 수 있습니다.

콘솔을 사용하려면 다음 안내를 따르세요.

  1. Firebase Console에서 '사용자' 표로 이동합니다.
  2. 여기를 클릭하세요
  3. 'flutterfire-ui-codelab' (또는 다른 이름을 사용한 경우 다른 프로젝트)을 선택합니다. 다음 표가 표시됩니다.

F038fd9a58ed60d9.png

  1. '사용자 추가' 버튼을 클릭합니다.

2d78390d4c5dbbfa.png

  1. 새 사용자의 이메일 주소와 비밀번호를 입력합니다. 아래 이미지에 입력한 것처럼 가짜 이메일과 비밀번호일 수 있습니다. 작동은 하지만 가짜 이메일 주소를 사용하면 '비밀번호 찾기' 기능이 작동하지 않습니다.

62ba0feb33d54add.png

  1. '사용자 추가'를 클릭합니다.

32b236b3ef94d4c7.png

이제 Flutter 애플리케이션으로 돌아가서 로그인 페이지를 통해 사용자를 로그인할 수 있습니다. 앱이 다음과 같이 표시됩니다.

dd43d260537f3b1a.png

6. 프로필 화면

FlutterFire UI는 ProfileScreen 위젯도 제공합니다. 이 위젯도 코드 몇 줄로 많은 기능을 제공합니다.

ProfileScreen 위젯 추가

텍스트 편집기에서 home.dart 파일로 이동합니다. 다음 코드로 업데이트하세요.

home.dart

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

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => const ProfileScreen(),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

새 참고 코드는 IconButton.isPressed method.에 전달되는 콜백입니다. 이 IconButton를 누르면 애플리케이션이 새 익명 경로를 만들고 해당 경로로 이동합니다. 이 경로는 MaterialPageRoute.builder 콜백에서 반환되는 ProfileScreen 위젯을 표시합니다.

앱을 새로고침하고 오른쪽 상단 (앱 바)에 있는 아이콘을 누르면 다음과 같은 페이지가 표시됩니다.

36487fc4ab4f26a7.png

FlutterFire UI 페이지에서 제공하는 표준 UI입니다. 모든 버튼과 텍스트 필드가 Firebase 인증에 연결되어 있으며, 즉시 작동합니다. 예를 들어 'Name' 텍스트 필드에 이름을 입력하면 FlutterFire UI가 FirebaseAuth.instance.currentUser?.updateDisplayName 메서드를 호출하여 Firebase에 해당 이름을 저장합니다.

로그아웃

지금은 '로그아웃' 버튼을 눌러도 앱이 변경되지 않습니다. 로그아웃되지만 AuthGate 위젯으로 다시 돌아가지는 않습니다. 이를 구현하려면 ProfileScreen.actions 매개변수를 사용합니다.

먼저 home.dart에서 코드를 업데이트합니다.

home.dart

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

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      })
                    ],
                  ),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

이제 ProfileScreen의 인스턴스를 만들 때 작업 목록을 ProfileScreen.actions 인수에 전달합니다. 이러한 작업은 FlutterFireUiAction 유형입니다. FlutterFireUiAction의 하위유형에는 여러 클래스가 있으며, 일반적으로 이러한 클래스를 사용하여 다양한 인증 상태 변경에 반응하도록 앱에 지시합니다. SignedOutAction은 Firebase 인증 상태가 currentUser인 null로 변경될 때 개발자가 제공하는 콜백 함수를 호출합니다.

SignedOutAction이 트리거될 때 Navigator.of(context).pop()를 호출하는 콜백을 추가하여 앱이 이전 페이지로 이동합니다. 이 예시 앱에는 로그인한 사용자가 없는 경우 로그인 페이지와 사용자가 있는 경우 홈페이지를 표시하는 영구 경로가 하나만 있습니다. 이 작업은 사용자가 로그아웃할 때 발생하므로 앱에서 로그인 페이지를 표시합니다.

프로필 페이지 맞춤설정

프로필 페이지는 로그인 페이지와 마찬가지로 맞춤설정할 수 있습니다. 첫째, 사용자가 프로필 페이지를 방문하면 현재 페이지에서 홈페이지로 돌아갈 수 있는 방법이 없습니다. ProfileScreen 위젯에 AppBar를 제공하여 이 문제를 해결합니다.

home.dart

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

class HomeScreen extends StatelessWidget {
 const HomeScreen({super.key});

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       actions: [
         IconButton(
           icon: const Icon(Icons.person),
           onPressed: () {
             Navigator.push(
               context,
               MaterialPageRoute<ProfileScreen>(
                 builder: (context) => ProfileScreen(
                   appBar: AppBar(
                     title: const Text('User Profile'),
                   ),
                   actions: [
                     SignedOutAction((context) {
                       Navigator.of(context).pop();
                     })
                   ],
                 ),
               ),
             );
           },
         )
       ],
       automaticallyImplyLeading: false,
     ),
     body: Center(
       child: Column(
         children: [
           Image.asset('dash.png'),
           Text(
             'Welcome!',
             style: Theme.of(context).textTheme.displaySmall,
           ),
           const SignOutButton(),
         ],
       ),
     ),
   );
 }
}

ProfileScreen.appBar 인수는 Flutter Material 패키지의 AppBar 위젯을 허용하므로 빌드하고 Scaffold에 전달한 다른 AppBar처럼 취급할 수 있습니다. 이 예에서는 '뒤로' 버튼을 자동으로 추가하는 기본 기능이 유지되고 화면에 제목이 있습니다.

프로필 화면에 하위 요소 추가

ProfileScreen 위젯에는 하위 요소라는 선택적 인수도 있습니다. 이 인수는 위젯 목록을 허용하며 이러한 위젯은 ProfileScreen을 빌드하는 데 내부적으로 이미 사용된 Column 위젯 안에 세로로 배치됩니다. ProfileScreen 빌드 메서드의 이 Column 위젯은 'Sign out' 버튼 위에 전달한 하위 요소를 배치합니다.

home.dart의 코드를 업데이트하여 로그인 화면과 비슷하게 여기에 회사 로고를 표시합니다.

home.dart

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

class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        actions: [
          IconButton(
            icon: const Icon(Icons.person),
            onPressed: () {
              Navigator.push(
                context,
                MaterialPageRoute<ProfileScreen>(
                  builder: (context) => ProfileScreen(
                    appBar: AppBar(
                      title: const Text('User Profile'),
                    ),
                    actions: [
                      SignedOutAction((context) {
                        Navigator.of(context).pop();
                      })
                    ],
                    children: [
                      const Divider(),
                      Padding(
                        padding: const EdgeInsets.all(2),
                        child: AspectRatio(
                          aspectRatio: 1,
                          child: Image.asset('flutterfire_300x.png'),
                        ),
                      ),
                    ],
                  ),
                ),
              );
            },
          )
        ],
        automaticallyImplyLeading: false,
      ),
      body: Center(
        child: Column(
          children: [
            Image.asset('dash.png'),
            Text(
              'Welcome!',
              style: Theme.of(context).textTheme.displaySmall,
            ),
            const SignOutButton(),
          ],
        ),
      ),
    );
  }
}

앱을 새로고침하면 화면에 다음과 같이 표시됩니다.

ebe5792b765dbf87.png

7. 멀티플랫폼 Google 인증 로그인

FlutterFire UI는 Google, Twitter, Facebook, Apple, GitHub 등의 서드 파티 제공업체를 인증하는 위젯과 기능도 제공합니다.

Google 인증과 통합하려면 기본 인증 흐름을 처리하는 공식 firebase_ui_oauth_google 플러그인과 종속 항목을 설치합니다. 터미널에서 Flutter 프로젝트의 루트로 이동하여 다음 명령어를 입력합니다.

flutter pub add google_sign_in
flutter pub add firebase_ui_oauth_google

Google 로그인 제공업체 사용 설정

그런 다음 Firebase Console에서 Google 제공업체를 사용 설정합니다.

  1. 콘솔에서 인증 로그인 제공업체 화면으로 이동합니다.
  2. '새 공급업체 추가'를 클릭합니다. 8286fb28be94bf30.png
  3. 'Google'을 선택합니다. C4e28e6f4974be7f.png
  4. '사용 설정'이라고 표시된 스위치를 전환하고 '저장'을 누릅니다. E74ff86990763826.png
  5. 구성 파일 다운로드에 대한 정보가 포함된 모달이 나타나면 "완료"를 클릭합니다.
  6. Google 로그인 제공업체가 추가되었는지 확인합니다. 5329ce0543c90d95.png

Google 로그인 버튼 추가

Google 로그인을 사용 설정한 상태에서 로그인 페이지에 양식화된 Google 로그인 버튼을 표시하는 데 필요한 위젯을 추가합니다. auth_gate.dart 파일로 이동하여 코드를 다음과 같이 업데이트합니다.

auth_gate.dart

import 'package:firebase_auth/firebase_auth.dart' hide EmailAuthProvider;
import 'package:firebase_ui_auth/firebase_ui_auth.dart';
import 'package:firebase_ui_oauth_google/firebase_ui_oauth_google.dart'; // new
import 'package:flutter/material.dart';

import 'home.dart';

class AuthGate extends StatelessWidget {
 const AuthGate({super.key});

 @override
 Widget build(BuildContext context) {
   return StreamBuilder<User?>(
     stream: FirebaseAuth.instance.authStateChanges(),
     builder: (context, snapshot) {
       if (!snapshot.hasData) {
         return SignInScreen(
           providers: [
             EmailAuthProvider(),
             GoogleProvider(clientId: "YOUR_WEBCLIENT_ID"),  // new
           ],
           headerBuilder: (context, constraints, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
           subtitleBuilder: (context, action) {
             return Padding(
               padding: const EdgeInsets.symmetric(vertical: 8.0),
               child: action == AuthAction.signIn
                   ? const Text('Welcome to FlutterFire, please sign in!')
                   : const Text('Welcome to Flutterfire, please sign up!'),
             );
           },
           footerBuilder: (context, action) {
             return const Padding(
               padding: EdgeInsets.only(top: 16),
               child: Text(
                 'By signing in, you agree to our terms and conditions.',
                 style: TextStyle(color: Colors.grey),
               ),
             );
           },
           sideBuilder: (context, shrinkOffset) {
             return Padding(
               padding: const EdgeInsets.all(20),
               child: AspectRatio(
                 aspectRatio: 1,
                 child: Image.asset('flutterfire_300x.png'),
               ),
             );
           },
         );
       }

       return const HomeScreen();
     },
   );
 }
}

여기서 유일하게 새로운 코드는 SignInScreen 위젯 구성에 GoogleProvider(clientId: "YOUR_WEBCLIENT_ID")를 추가한 것입니다.

추가한 후 앱을 새로고침하면 Google 로그인 버튼이 표시됩니다.

Aca71a46a011bfb5.png

로그인 버튼 구성

추가 구성이 없으면 버튼이 작동하지 않습니다. Flutter Web으로 개발하는 경우 이 단계만 추가하면 됩니다. 다른 플랫폼에는 추가 단계가 필요하며, 이에 대해서는 잠시 후에 설명합니다.

  1. Firebase Console에서 인증 제공업체 페이지로 이동합니다.
  2. Google 제공업체를 클릭합니다. 9b3a325c5eca6e49.png
  3. 'Web SDK configuration' 확장 패널을 클릭합니다.
  4. '웹 클라이언트 ID' 711a79f0d931c60f.png의 값을 복사합니다.
  5. 텍스트 편집기로 돌아가서 이 ID를 clientId라는 매개변수에 전달하여 auth_gate.dart 파일의 GoogleProvider 인스턴스를 업데이트합니다.
GoogleProvider(
   clientId: "YOUR_WEBCLIENT_ID"
)

웹 클라이언트 ID를 입력하고 앱을 새로고침합니다. 'Google 계정으로 로그인' 버튼을 누르면 Google 로그인 과정을 안내하는 새 창이 표시됩니다 (웹을 사용하는 경우). 처음에는 다음과 같습니다.

14e73e3c9de704bb.png

iOS 구성

iOS에서 이 작업을 수행하려면 추가 구성 프로세스가 있습니다.

  1. Firebase Console에서 프로젝트 설정 화면으로 이동합니다. 다음과 같은 Firebase 앱 목록을 표시하는 카드가 표시됩니다. fefa674acbf213cc.png
  2. iOS를 클릭합니다. 애플리케이션 이름은 저와 다릅니다. flutter-codelabs/firebase-auth-flutterfire-ui/start 프로젝트를 사용하여 이 Codelab을 진행한 경우 'complete'는 'start'라고 표시됩니다.
  3. 'GoogleServices-Info.plist'라고 표시된 버튼을 클릭하여 필요한 구성 파일을 다운로드합니다. F89b3192871dfbe3.png
  4. 다운로드한 파일을 라는 디렉토리로 드래그 앤 드롭합니다 ./ios/Runner를 설정합니다.
  5. 프로젝트 루트에서 다음 터미널 명령어를 실행하여 Xcode를 엽니다.

ios/Runner.xcworkspace 열기

  1. Runner 디렉터리를 마우스 오른쪽 버튼으로 클릭하고 Add Files to "Runner"를 선택합니다. 858986063a4c5201.png
  2. 파일 관리자에서 GoogleService-Info.plist를 선택합니다.
  3. 텍스트 편집기 (Xcode가 아님)로 돌아가서 아래의 CFBundleURLTypes 속성을 [my_project]/ios/Runner/Info.plist 파일에 추가합니다.
<!-- Put me in the [my_project]/ios/Runner/Info.plist file -->
<!-- Google Sign-in Section -->
<key>CFBundleURLTypes</key>
<array>
        <dict>
                <key>CFBundleTypeRole</key>
                <string>Editor</string>
                <key>CFBundleURLSchemes</key>
                <array>
                        <!-- TODO Replace this value: -->
                        <!-- Copied from GoogleService-Info.plist key REVERSED_CLIENT_ID -->
                        <string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
                </array>
        </dict>
</array>
<!-- End of the Google Sign-in Section -->

Flutter 앱이 이미 iOS에서 실행 중인 경우 앱을 완전히 종료한 후 애플리케이션을 다시 실행해야 합니다. 그 외의 경우에는 iOS에서 앱을 실행합니다.

8. 수고하셨습니다.

Flutter용 Firebase 인증 UI Codelab을 완료했습니다 . 이 Codelab의 완성된 코드는 GitHub의 'complete' 디렉터리(Flutter Codelabs)에서 확인할 수 있습니다.

학습한 내용

  • Firebase를 사용하도록 Flutter 앱 설정
  • Firebase Console에서 Firebase 프로젝트 설정
  • FlutterFire CLI
  • Firebase CLI
  • Firebase 인증 사용
  • FlutterFire UI를 사용하여 Flutter 앱에서 Firebase 인증 손쉽게 처리

다음 단계

자세히 알아보기

Sparky가 함께 축하해 주셨습니다.

2a0ad195769368b1.gif