iT邦幫忙

2023 iThome 鐵人賽

DAY 13
1
Mobile Development

Flutter 從零到實戰 - 30 天の學習筆記系列 第 13

[Day 13] 實戰新聞 APP - Google Sign In 實作

  • 分享至 

  • xImage
  •  

今天我們要來實際串接 Google 的第三方登入方式,不過在此之前我們有必要來重整一下我們的程式碼。

重整程式碼

我們在上一章節中,我們實作了「登入」與「註冊」的兩個功能。仔細想一下,不難發現兩者做的事情非常相像:

  1. 兩者都是與帳號服務相關的內容
  2. 兩者都是一旦有發生錯誤時,會跳出通知
  3. 通知的函式 _showAlertDialog 我們分別在兩個檔案中寫了兩次,但做的明明只是同一件事情

因此在我們開始寫第三方登入之前,我們有必要整併當前的內容,使得接下來延伸開發的第三方登入功能也可以寫在其中。

請在 lib 底下建立一個 repositories 資料夾,並建立一個新檔案名叫 auth_repository.dart。此檔案將用於放置一切與驗證相關的對外獲取資料統一接口。

void _showAlertDialog(...) { ... 彈出通知視窗程式碼 }

class AuthRepository {
  Future<bool> signInWithEmailAndPassword({
    required BuildConext context, 
    required String email, 
    required String password
  }) { ... 登入相關程式碼 }
  Future<bool> singUpWithEmailAndPassword({
    required BuildContext context,
    required String email,
    required String password,
    required String confirmPassword
  }) { ... 註冊相關程式碼 }
}

各位讀者可以自行練習,嘗試以上方的方式來進行程式碼的整理。如此在登入頁面中的「登入」按鈕的程式碼便可以簡化成以下形式;於註冊頁面亦然。

onPressed() {
  AuthRepository().signInWithEmailAndPassword(
    context: context, 
    email: _emailController.text, 
    password: _passwordController.text
  );
}

整理的差不多了,我們就繼續往下拉~

設定 Google Sign-in

請先打開 Flutter 專案,並輸入以下指令來下載套件

flutter pub add google_sign_in

首先請先至firebase console → authentication → Sign-in method 頁面點選「添加新提供方」,並點選 Google,啟用並「保存」。畫面如下:

https://ithelp.ithome.com.tw/upload/images/20230928/201350824ipyyMWB9p.png

接下來我們會分開講解 Android 與 iOS 的設定方式,因為分別有不同的設定方法。

Android

  1. 至專案主頁,並點開 android 的應用程式

    https://ithelp.ithome.com.tw/upload/images/20230928/2013508218LjgKmig4.png

    會開啟以下視窗,請點選下載 google_services.json 並放置於 Flutter 專案中 android/app 底下。原先資料夾已經有該檔案,可以直接取代。

    https://ithelp.ithome.com.tw/upload/images/20230928/20135082K9xygRfDGO.png

  2. 可以先執行 Android 模擬器試試看,如果能執行可以忽略此兩個步驟。不過我自己在這個階段執行起來會發生 :app:mapDebugSourceSetPaths 的錯誤。

    https://ithelp.ithome.com.tw/upload/images/20230928/20135082pHXF2Pd4t4.png

    這個錯誤是起因於 google servicesgradle 的版本不相容所導致,我們需要手動來進行版本更動。請打開 android/build.gradle 檔案並找到 dependencies 的部分如下:

    dependencies {
        classpath 'com.android.tools.build:gradle:7.3.0'
        // START: FlutterFire Configuration
        // 我的預載版本為 4.3.10,改成 4.3.15
        classpath 'com.google.gms:google-services:4.3.15'
        // END: FlutterFire Configuration
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
    

    參考來源:https://developers.google.com/android/guides/google-services-plugin?hl=en

  3. 請再執行看看,如果能執行那可以忽略這個步驟。不過我在這個階段會發生 :app:mergeDexDebug 錯誤。

    https://ithelp.ithome.com.tw/upload/images/20230928/20135082uULaiEUDb1.png

    這個錯誤的引發原因是因為在執行 APP 前,Android 會將程式打包成 APK,而 dex 文件為 Android 打包工具(APK)的一部分,但受限於單個 dex 文件最多只能為 64k 個方法。因此我們需要使用拆解成多個 dex 文件來突破限制 (MultiDex)。請打開 android/app/build.gradle 檔案並找到 defaultConfig 的部分如下:

    defaultConfig {
        applicationId "com.example.micro_news_tutorial"
        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
        // 添加這行
        multiDexEnabled true
    }
    
  4. 接著我們還需要進行應用程式的身份驗證,請打開終端機並輸入以下指令

    # mac or linux
    keytool -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
    # windows
    keytool -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore
    

    此時 keytool 會提示輸入密碼,輸入 android 即可。接著 keytool 就會回傳 SHA1SHA 256 的指紋至終端機。請將指紋貼至下方「新增指紋」跳出的輸入框中。

    https://ithelp.ithome.com.tw/upload/images/20230928/20135082DewwBHLTX3.png

  5. 大功告成拉~~

不得不說... 光要找步驟 2 和 步驟 3 的解決方法花了我一堆時間XDD,希望當你遇到問題時,上方的步驟可以幫助到你。

iOS

  1. 至專案主頁,並點開 ios 的應用程式

    https://ithelp.ithome.com.tw/upload/images/20230928/201350826aST1QLmE2.png

    會開啟以下視窗,請點選下載 GoogleService-Info.plist 並放置於 Flutter 專案中 ios/Runner 底下。原先資料夾已經有該檔案,可以直接取代。

    https://ithelp.ithome.com.tw/upload/images/20230928/20135082Ym32rGXyAZ.png

  2. 請打開 ios/Runner/Info.plist 檔案,並將下方程式碼於空白處貼上。再從 GoogleService-Info.plist 複製 REVERSED_CLIENT_ID 並替換下面指示的地方。

    <key>CFBundleURLTypes</key>
    <array>
        <dict>
            <key>CFBundleTypeRole</key>
            <string>Editor</string>
            <key>CFBundleURLSchemes</key>
            <array>
                <!-- 從 GoogleService-Info.plist 找到 REVERSED_CLIENT_ID 並取代下面這行 -->
                <string>com.googleusercontent.apps.861823949799-vc35cprkp249096uujjn0vvnmcvjppkn</string>
            </array>
        </dict>
    </array>
    
  3. 因為安裝新的套件的緣故,很可能會再一次導致 iOS 打包時無法執行 pod install 而失敗,這時可以使用我們上一篇教的方法,照著執行一次就可以順利執行囉。

串接 Google Sign-in

費了一番心力,終於完成了 google 登入的設定,現在我們要來將功能串上應用程式。

在本篇的最開始有請各位讀者先將驗證相關的程式碼整理至 auth_service.dart 檔案中。現在我們要接續此來進行開發,請參考以下範例程式碼:

class AuthRepository {
  // 以上省略
  Future<bool> signInWithGoogle({required BuildContext context}) async {
    try {
      // 跳出 google 登入視窗
      final GoogleSignInAccount? googleUser = await GoogleSignIn().signIn();
      // 判斷該視窗關閉後是否有選定帳號
      if (googleUser != null) {
        final GoogleSignInAuthentication googleAuth =
            await googleUser.authentication;
        final credential = GoogleAuthProvider.credential(
            accessToken: googleAuth.accessToken, idToken: googleAuth.idToken);
        await FirebaseAuth.instance.signInWithCredential(credential);
        return true;
      } else {
        // 通常發生這個行為都是使用者按了取消
        throw Exception("登入失敗");
      }
    } catch (e) {
      _showAlertDialog(context, "登入失敗", "請稍後再試");
      return false;
    }
  } 
}

透過以上程式碼可以實現 Google 登入,並得益於我們將 _showAlertDialog 整併在同一檔案中,因此一旦登入階段發生錯誤也可以實時的跳出通知。

Apple Sign-in

雖然我們在畫面上有留存透過 Apple 帳號進行登入的第三方驗證方式,但是受限於欲使用該服務前需要先加入 Apple Developer Program,並且該服務是要付錢的,也就因此這部分的實作我們只能忍痛先暫時跳過 QQ ,畢竟在我還沒有實際上架應用程式至 App Store 前先暫時不考慮花這一筆...

不過未來我將會持續的更新此系列的文章,說不定哪天等你回來看的時候,我已經把這部分的步驟也寫好拉~就敬請期待囉!

忘記密碼頁面

這是登入流程的最後一塊拼圖,也就是我們一直尚未提及的「忘記密碼」頁面。畢竟我們的應用程式還是有提供註冊的功能,因此此部分絕對不能少XDD。

請參考我們的設計稿「忘記密碼頁面」。其實與註冊、帳號大體相似,同樣有輸入框、按鈕,不過上方的圖片由鎖替換成信封的圖片。

因此請在 screens 底下創建一個新檔案 forget_password_screen.dart ,並創建一個 stateful widget。

程式碼的部分就當作讓讀者自行練習,現在對您來說應該相當簡單了XDD。唯一需要講述的在於流程。

使用者於「登入頁面」點擊忘記密碼時進入「忘記密碼頁面」。一旦輸入完信箱並按下送出後,系統要自動寄出重設密碼郵件的同時,跳回登入頁面。

讓我們來實現這個流程吧!首先是處理寄送重設密碼信件的程式碼:

class AuthRepository {
  // 以上省略
  Future<bool> sendPasswordResetEmail(
      {required BuildContext context, required String email}) async {
    try {
      await FirebaseAuth.instance.sendPasswordResetEmail(email: email);
      return true;
    } on FirebaseAuthException catch (e) {
      if (e.code == 'invalid-email') {
        _showAlertDialog(context, '電子郵件格式錯誤', '請檢查您的電子郵件格式是否正確');
      } else if (e.code == 'user-not-found') {
        _showAlertDialog(context, '找不到使用者', '請檢查您的電子郵件是否正確');
      } else {
        _showAlertDialog(context, '發生錯誤', '請稍後再試');
      }
      return false;
    }
  } 
}

接下來是觸發此函式的忘記密碼頁面中「送出」按鈕事件:

onPressed: () {
  AuthRepository()
    .sendPasswordResetEmail(
        context: context, email: _emailController.text)
    .then((state) => {
      // 只有函式回傳為 true 時才跳回上頁
      if (state) {
        Navigator.pop(context)
      }
    });
},

這麼一來,就可以成功的完成我們所需要的流程。

馬上測試看看吧!如果順利的話你會在信箱中收到重設密碼的驗證信。

今日總結

我們花了三天的時間就將登入頁面、註冊頁面以及忘記密碼頁面的畫面完成與串上實際的功能,此外我們還支援了超方便的第三方登入方式。雖然 Apple sign-in 暫時無法使用有點遺憾,但已經很棒拉!

明天我們就要進入登入後的新聞畫面實作拉。各位敬請期待呦!

今天的參考程式碼:https://github.com/ChungHanLin/micro_news_tutorial/tree/day13/micro_news_app


上一篇
[Day 12] 實戰新聞 APP - 設定 Firebase Auth 與串接
下一篇
[Day 14] 實戰新聞 APP - 導覽列、主題與字體設定
系列文
Flutter 從零到實戰 - 30 天の學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言