今天我們要來實際串接 Google 的第三方登入方式,不過在此之前我們有必要來重整一下我們的程式碼。
我們在上一章節中,我們實作了「登入」與「註冊」的兩個功能。仔細想一下,不難發現兩者做的事情非常相像:
_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
);
}
整理的差不多了,我們就繼續往下拉~
請先打開 Flutter 專案,並輸入以下指令來下載套件
flutter pub add google_sign_in
首先請先至firebase console → authentication → Sign-in method 頁面點選「添加新提供方」,並點選 Google,啟用並「保存」。畫面如下:
接下來我們會分開講解 Android 與 iOS 的設定方式,因為分別有不同的設定方法。
至專案主頁,並點開 android
的應用程式
會開啟以下視窗,請點選下載 google_services.json
並放置於 Flutter 專案中 android/app
底下。原先資料夾已經有該檔案,可以直接取代。
可以先執行 Android 模擬器試試看,如果能執行可以忽略此兩個步驟。不過我自己在這個階段執行起來會發生 :app:mapDebugSourceSetPaths
的錯誤。
這個錯誤是起因於 google services
與 gradle
的版本不相容所導致,我們需要手動來進行版本更動。請打開 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
請再執行看看,如果能執行那可以忽略這個步驟。不過我在這個階段會發生 :app:mergeDexDebug
錯誤。
這個錯誤的引發原因是因為在執行 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
}
接著我們還需要進行應用程式的身份驗證,請打開終端機並輸入以下指令
# 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 就會回傳 SHA1
與 SHA 256
的指紋至終端機。請將指紋貼至下方「新增指紋」跳出的輸入框中。
大功告成拉~~
不得不說... 光要找步驟 2 和 步驟 3 的解決方法花了我一堆時間XDD,希望當你遇到問題時,上方的步驟可以幫助到你。
至專案主頁,並點開 ios
的應用程式
會開啟以下視窗,請點選下載 GoogleService-Info.plist
並放置於 Flutter 專案中 ios/Runner
底下。原先資料夾已經有該檔案,可以直接取代。
請打開 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>
因為安裝新的套件的緣故,很可能會再一次導致 iOS 打包時無法執行 pod install
而失敗,這時可以使用我們上一篇教的方法,照著執行一次就可以順利執行囉。
費了一番心力,終於完成了 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 帳號進行登入的第三方驗證方式,但是受限於欲使用該服務前需要先加入 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