# Flutter開發之旅 在使用firebase時,會需要initializeApp,但是官方的文檔並沒有辦法直接使用,在stackoverflow查到,因為Firebase.initializeApp(),需要使用到底層的物件,這是一個異步方法,因此我們需要確保初始化被確實執行。 需要這樣寫 void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } https://stackoverflow.com/questions/63873338/what-does-widgetsflutterbinding-ensureinitialized-do 但我這樣用也可以XD,不知道是不是他有handle void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp(App()); } ## google sign in 註冊to firebase,使用emulator。 4/2 終於搞定了登入註冊的問題,搞了整整3天....結果問題是localhost權限忘記打開。 ![](https://i.imgur.com/spnZtls.png) 總結一下遇到的所有問題:一開始想說用firebase來當作資料庫,建立帳號登入系統,但是得要使用3個有點不相關的套件合作才能達成。 1. firebase_core 1. firebase_auth 1. google_sign_in 三個分別是什麼呢 ? ### firebase_core firebase_core是用來讓dart與firebase套件能運作的整合API,在使用一切firebase操作之前必須call firebase_core的初始化函數: ``` Firebase.initializeApp(); ``` 有許多call法: 1. 使用future來確保初始化完成,才建立widget ``` @override Widget build(BuildContext context) { return FutureBuilder( // Initialize FlutterFire future: Firebase.initializeApp(), builder: (context, snapshot) { // Check for errors if (snapshot.hasError) { return Container( color:Colors.green ); } // Once complete, show your application if (snapshot.connectionState == ConnectionState.done) { return MaterialApp( title:'Google Sign In', home: SignInDemo(), ); } // Otherwise, show something whilst waiting for initialization to complete return Container( color:Colors.purple ); }, ); } ``` 2. 在main()函數中使用,但需要call ```WidgetsFlutterBinding.ensureInitialized();``` ``` void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } ``` 這是使用底層API去呼叫flutter必須先完成初始化任務才能繼續執行。詳細說明在stackoverflow有解釋:https://stackoverflow.com/questions/63873338/what-does-widgetsflutterbinding-ensureinitialized-do ### firebase_auth firebase_auth是firebase Authentication的API,負責進行Authentication的資料與認證,是一個註冊和登入的interface。 這幾天主要也是卡在這個地方... 大概用法是 ``` FirebaseAuth auth = FirebaseAuth.instance; //先建立一個instance auth.Method() //即可執行auth的相關操作。 ``` 詳閱api文檔 : https://firebase.flutter.dev/docs/auth/overview 底下是登入註冊方法的範例: ``` Future<void> _handleSignIn() async { try { final googleUser = await _googleSignIn.signIn(); final GoogleSignInAuthentication googleAuth = await googleUser!.authentication; // Create a new credential. final googleCredential = GoogleAuthProvider.credential( accessToken: googleAuth.accessToken, idToken: googleAuth.idToken, ); // Sign in to Firebase with the Google [UserCredential]. final UserCredential googleUserCredential = await auth.signInWithCredential(googleCredential); 上面這一行使用google的Credential來進行註冊firebase認證。 } catch (error) { print(error); } } ``` 然後主要有一個問題:我如果只想要在測試環境底下操作而不是生產環境,則必須建立一個firebase emulator。 使用法: ``` class SignInDemoState extends State<SignInDemo> { GoogleSignInAccount? _currentUser; String _contactText = ''; FirebaseAuth auth = FirebaseAuth.instance; //這裡先建立一個instance @override void initState() { super.initState(); auth.useEmulator('http://localhost:9099'); 在initState的地方打上這行 ,即可將操作轉為對emulator使用。文檔的要求只有在操作以前call他就好 底下省略... ``` 在這裡其實有一個問題存在如果我在非function的地方寫**auth.useEmulator('http://localhost:9099');** 其實類別會無法辨認**auth**是什麼,所以Scope要嚴格。 ### null Safety 在新的google sign in API裡必須要有null Safety保護變數不是null。因此要在變數上加入保護機制才不會報錯。 比如這行,googleUser後面有一個 ! GoogleSignInAccount? account 有一個? ``` final GoogleSignInAuthentication googleAuth = await googleUser!.authentication; _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? account) ``` ### google_sign_in google官方的東西就是特別嚴謹... 很多資訊你是沒辦法從原始碼讀懂的,以及一些東西甚至涉及到設備原生操作,基本上是一個黑盒子。 這段可以呼叫google的登入流程。 ``` final googleUser = await _googleSignIn.signIn(); ``` 我是這樣發現的:只有在完成一切登入過程後console才會print出來*signin玩了喔*,由此證明整個過程google_sign_in API已經幫我們搞定了 ``` final googleUser = await _googleSignIn.signIn(); print('signin玩了喔'); ``` ## FireStore 4/5 今天在使用firestore的時候居然報了一個錯誤Error while merging dex archives: The number of method references in a .dex file cannot exceed 64K. 意思是說我的程式裡頭import了超過64K數量方法的檔案。看來firestore有夠大的... 查了一下發現官方有提到如何規避 为方法数超过 64K 的应用启用 MultiDex https://developer.android.com/studio/build/multidex 在./app/build.gradle裡面加上 ![](https://i.imgur.com/30AXSn8.png) ![](https://i.imgur.com/MosnWRr.png) ~~wwwwwww 還沒結束,今天還遇到一個問題...花了7小時去查...完全沒找到任何討論,結果是emulator初始化忘了打 ~~我把firebase init emulators 一直打錯成firebase init ~~以至於完全沒寫入到emulator上,雖然我不知道為什麼這樣在使用真實的firestore上有問題。。。~~ ~~ hi 4/7 這個問題又發生了,貌似並非少打emulators的問題,而是android模擬器髒掉了.... 重新砍掉再開一個新的第一次就成功了。 6/1. Localhost請求 一般來說,只有在測試階段才會對localhost去做請求,但是這時候會出現錯誤,原因有兩個:必須要在 1. 對localhost請求的網址必須從 http://localhost 變為 http://10.0.2.2 2. 必須要在debug/AndroidManifest.xml中添加以下: <application android:usesCleartextTraffic="true"> <!-- possibly other elements --> </application> 寫法紀錄 @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(), body: ListView( children: [ for (var book in books) ListTile( title: Text(book.title), subtitle: Text(book.author), onTap: () => onTapped(book), ) ],