김마드 2023. 12. 6. 13:07

1. firebase cli 및 sdk를 이용하기 위해 몇 가지 세팅을 진행하자.

 

https://firebase.google.com/docs/flutter/setup?hl=ko&platform=ios

 

Flutter 앱에 Firebase 추가

의견 보내기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Flutter 앱에 Firebase 추가 plat_ios plat_android plat_web iOS+ Android 웹 기본 요건 아직 Flutter 앱이 없다면

firebase.google.com

 

1) npm install -g firebase-tools 설치하고 firebase 명령어에 입력 후 화면이 나오면 설치 성공

2) firebase login으로 파베 계정 로그인

3) dart pub global activate flutterfire_cli 설치

4) flutterfire configure 입력하면 실행할 수 없다고 나오는데, 다시 dart pub global activate flutterfire_cli 를 입력하면 path가 없다고 나옴 -> 시스템 환경 변수 path에 해당 경로 추가 하고 vsc 껐다 키기

5) 다시 flutterfire configure 를 입력하면 본인이 원하는 플랫폼(android, ios) 선택하고 엔터

--> 여기까지가 일반 초기화 세팅

 

6) flutter pub add firebase_core 설치

7) flutter pub add firebase_auth 설치 (위 파베 사이트 매뉴얼에는 없으나 auth를 하기 위해서 설치)

8) flutterfire configure로 한번 더 세팅 (플랫폼 동일하게)

그리고 아래와 같이 세팅

 

 

2. 파베 로그인 기능을 활용하기 위해 파베에서 몇가지 세팅을 하자

 

프로젝트를 새로 만들고, authentication 부분에 이메일/비밀번호부분을 선택하자. (구글,페북 로그인 등 다른 방법도 있음)

 

 

 

- main.dart

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

import 'firebase_options.dart';
import 'package:chat_app/screens/auth.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FlutterChat',
      theme: ThemeData().copyWith(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
            seedColor: const Color.fromARGB(255, 63, 17, 177)),
      ),
      home: const AuthScreen(),
    );
  }
}

 

1) 파베 설치 이후, main 부분을 위와 같이 세팅해주고 재실행해준다. 파베 설치하면 아래 파일도 생성될텐데, 이건 일단 그대로 두자.

 

 

- screens/auth.dart

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

final _firebase = FirebaseAuth.instance;

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

  @override
  State<AuthScreen> createState() {
    return _AuthScreenState();
  }
}

class _AuthScreenState extends State<AuthScreen> {
  final _form = GlobalKey<FormState>();

  var _isLogin = true;
  var _enteredEmail = '';
  var _enteredPassword = '';

  void _submit() async {
    final isValid = _form.currentState!.validate();

    if (!isValid) {
      return;
    }

    _form.currentState!.save();

    if (_isLogin) {
      // log users in
    } else {
      try {
        final userCredentials = await _firebase.createUserWithEmailAndPassword(
            email: _enteredEmail, password: _enteredPassword);
        print(userCredentials);
      } on FirebaseAuthException catch (error) {
        if (error.code == 'email-already-in-use') {
          // ...
        }
        ScaffoldMessenger.of(context).clearSnackBars();
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(error.message ?? 'Authentication failed.'),
          ),
        );
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Theme.of(context).colorScheme.primary,
      body: Center(
        child: SingleChildScrollView(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                margin: const EdgeInsets.only(
                  top: 30,
                  bottom: 20,
                  left: 20,
                  right: 20,
                ),
                width: 200,
                child: Image.asset('assets/images/chat.png'),
              ),
              Card(
                margin: const EdgeInsets.all(20),
                child: SingleChildScrollView(
                  child: Padding(
                    padding: const EdgeInsets.all(16),
                    child: Form(
                      key: _form,
                      child: Column(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          TextFormField(
                            decoration: const InputDecoration(
                                labelText: 'Email Address'),
                            keyboardType: TextInputType.emailAddress,
                            autocorrect: false,
                            textCapitalization: TextCapitalization.none,
                            validator: (value) {
                              if (value == null ||
                                  value.trim().isEmpty ||
                                  !value.contains('@')) {
                                return 'Please enter a valid email address.';
                              }

                              return null;
                            },
                            onSaved: (value) {
                              _enteredEmail = value!;
                            },
                          ),
                          TextFormField(
                            decoration:
                                const InputDecoration(labelText: 'Password'),
                            obscureText: true,
                            validator: (value) {
                              if (value == null || value.trim().length < 6) {
                                return 'Password must be at least 6 characters long.';
                              }
                              return null;
                            },
                            onSaved: (value) {
                              _enteredPassword = value!;
                            },
                          ),
                          const SizedBox(height: 12),
                          ElevatedButton(
                            onPressed: _submit,
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Theme.of(context)
                                  .colorScheme
                                  .primaryContainer,
                            ),
                            child: Text(_isLogin ? 'Login' : 'Signup'),
                          ),
                          TextButton(
                            onPressed: () {
                              setState(() {
                                _isLogin = !_isLogin;
                              });
                            },
                            child: Text(_isLogin
                                ? 'Create an account'
                                : 'I already have an account'),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

 

1) 기본적 form 사용법은 이전 강의와 동일하다.

2)

    if (_isLogin) {
      // log users in
    } else {
      try {
        final userCredentials = await _firebase.createUserWithEmailAndPassword(
            email: _enteredEmail, password: _enteredPassword);
        print(userCredentials);
      } on FirebaseAuthException catch (error) {
        if (error.code == 'email-already-in-use') {
          // ...
        }
        ScaffoldMessenger.of(context).clearSnackBars();
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(
            content: Text(error.message ?? 'Authentication failed.'),
          ),
        );
      }
    }
  }

-firebase.createUserWithEmailAndPassword의 sdk를 통해서 쉽게 이메일과 패스워드로 회원가입이 가능하다. 

 

-이 부분에서 그냥 catch가 아닌 on FirebaseAuthException을 붙인 이유는, 에러를 firebase에서 제공하는 에러만 잡겠다는 의미다. createUserWithEmailAndPassword에 마우스를 올려놓으면 에러코드들을 확인 할 수 있다.

 

- 위 방법을 통해 회원가입을 하면, 파베에 아래와 같이 계정이 등록된다. 파베 유저 관리 부분에는 비밀번호 재설정 이메일 보내기 등 기본적인 서비스들을 제공해준다.

 

- 스낵바를 이용해 에러 메시지 보여준다.

 

3) 나머지 부분은 눈으로 따라가 보면 이해 된다.

 

- 결과 화면