---
# System prepended metadata

title: Flutter開發之旅

---

# 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),
            )
        ],