---
# System prepended metadata

title: Firebase + Flutter 簡單實作經驗筆記
tags: [flutter]

---

# Firebase + Flutter 簡單實作經驗筆記
Firebase是一個提供存取、帳戶應用、App監控的一個免費平台
>https://console.firebase.google.com/?hl=zh-cn

今天要操作的是使用Flutter實現在**Firebase上達成手機註冊、登入與監聽User**的部分。

* 一些小觀念提點
1. 在firebase當中再實現手機操作會分成是在 "手機端"、"Web" 的部分
 ```dart=
 verifyPhoneNumber + signInWithCredential + PhoneAuthProvider.credential     //手機端
 signInWithPhoneNumber //Web端
 ```
2. 可以使用監聽的方式進行監控View跟model的部分ex. User是否登入登出。

> 接下來都是以你已經成功連接Flutter + firebase後的實例
> UI構成部分，就只要有簡單幾個輸入框跟按鈕即可
### 1.創建一個能夠呼叫firebase函式庫的語法
```dart=
final _auth = FirebaseAuth.instance; 
//.instance為單例模式(此生命週期舊只會有這一次的new實例，其餘的創建都會是應用於這個實例
```
### 2.註冊帳號
1. 使用email + password
可以在controller的部分寫上註冊方法，並且創建AuthRepository實例呼叫當中的自訂方法createUserWithEnailAndPassword。
``` dart=
void registerUser(String email, String password) {
    AuthRepository authRepository = AuthRepository();
    authRepository.createUserWithEnailAndPassword(email, password);
  }
  
class AuthRepository extends ChangeNotifier{
...
  Future<void> createUserWithEnailAndPassword(
    String email,
    String password,
  ) async {
    try {
      await _auth.createUserWithEmailAndPassword(
          email: email, password: password);
    } on FirebaseAuthException catch (e) {
      debugPrint(e.code);
    } catch (_) {}
  }
}
```
假如成功完成的話就會在你連結的firebase上出現帳戶
![image](https://hackmd.io/_uploads/rykQCCk0T.png)
2. 手機註冊
>重點提醒! 不管註冊還是登入都會是走verifyPhoneNumber + signInWithCredential( PhoneAuthProvider.credential)的流程喔。
>假如執行到signInWithCredential(PhoneAuthProvider.credential)時沒有帳戶的話就會直接新增一個上去，有的話就會進入登入動作

1. verifyPhoneNumber先驗證手機正確性
**在完成呼叫verifyPhoneNumber之後他會傳OTP碼到你設定的phoneNumber當中**
所以可以再codesent的部分回傳一些東西或是使用Provider去監聽去改變頁面到輸入OTP碼的頁面。
>verificationId將會是接下來登入的ID要先存起來
``` dart=
void registerUserByPhone(String phoneNumber, BuildContext context) {
    AuthRepository authRepository = AuthRepository();
    authRepository.phoneAuthentication(phoneNumber, context);
  }
class AuthRepository with ChangeNotifier {
  final _auth = FirebaseAuth.instance;
  User? user = FirebaseAuth.instance.currentUser;

  String? verificationId;

  void updateVerificationId(var value) {
    verificationId = value;
    notifyListeners();
  }
  
  
...


  ///手機號碼註冊帳號的方式
  ///1. 先用verifyPhoneNumber先驗證手機正確性
  ///2. 獲取到verificationId後再
  ///3. 創建PhoneAuthCredential使用signInWithCredential進行登入或註冊

  Future<void> phoneAuthentication(
    String phoneNumber,
    BuildContext context,
  ) async {
    final authVerify = Provider.of<AuthRepository>(context, listen: false);

    _auth.verifyPhoneNumber(
      phoneNumber: phoneNumber,
      verificationCompleted: (credential) async {
        debugPrint("我進入到了verificationCompleted");
        await _auth.signInWithCredential(credential);
      },
      verificationFailed: (exception) {
        debugPrint("firebase手機登入驗證，號碼失敗，$exception");
      },
      codeSent: (String verificationId, int? resendToken) async {
        debugPrint("firebase手機登入驗證，verificationId: $verificationId");

        authVerify.updateVerificationId(verificationId);
        
      },
      codeAutoRetrievalTimeout: (String verificationId) {},
    );
  }
}
```
2. 驗證OTP碼 + 登入(註冊)

將剛剛的verificationId與otp碼同時輸入給signInWithCredential(PhoneAuthProvider.credential())就完成登入(註冊)了。

``` dart=
 void verifyOTP(
    String verificationId,
    String otp,
    BuildContext context,
  ) {
    AuthRepository authRepository = AuthRepository();

    authRepository
        .verifyOTP(
      verificationId,
      otp,
      context,
    )
        .then((value) {
      value ? debugPrint("OTP成功") : debugPrint("OTP失敗");
    });
  }
  
  class AuthRepository with ChangeNotifier {
  
  ...
  
  Future<bool> verifyOTP(
    String verificationId,
    String otp,
    BuildContext context,
  ) async {
    var credentials = await _auth.signInWithCredential(
      PhoneAuthProvider.credential(
          verificationId: verificationId, smsCode: otp),
    );

    return credentials.user != null ? true : false;
  }
}

```
### 3.監聽的部分

因為不管是登入還是登出Firebase都會有一段小延遲時間，因此為了避免會有問題，就可以使用到監聽手段在UI更新頁面而去觸發其他的更新資料之類的。
>記得有使用到監聽就要去清除。
``` dart=
  StreamSubscription<User?>? _authSubScription;

  @override
  void initState() {
    super.initState();
    _authSubScription =
        FirebaseAuth.instance.authStateChanges().listen((User? user) {
      final userData = Provider.of<UserData>(context, listen: false);
      if (user == null) {
        userData.updateUserUuid(user?.uid);
      } else {
        userData.updateUserUuid(user.uid);
      }
    });
  }

  @override
  void dispose() {
    _authSubScription!.cancel();
    super.dispose();
  }
```


完整程式碼
https://drive.google.com/drive/folders/1n-iTPyvRCK3SEXicGSGg5ed6Tg1TsIqR?usp=sharing



---


參考資料
firebase文檔
https://firebase.google.com/docs/flutter/setup?hl=zh-cn&platform=android
https://firebase.google.com/docs/auth/flutter/phone-auth?hl=zh-cn
https://firebase.google.com/docs/auth/flutter/password-auth?hl=zh-cn
youtube影片(內含GetX語法、但我自己是使用Provider)
https://youtu.be/dGwr66EkJKk?si=EiJ9kVnAgsdW6IkK


