Flutter×Firebase 開発チップ集

Flutter×Firebaseで開発をする上で役立ちそうな内容をまとめた。
随時更新予定。

## Flutter設定(全般)

### パッケージ名の指定
`--org`オプションを指定することでパッケージ名を指定することができる。
デフォルトだと`com.example.{アプリ名}`という形で生成されてしまう。
もちろん後で書換えることも可能だが、パッケージ名が予め決まっているならプロジェクト作成時に指定するのが一番手間が少なくて良い。

```bash
flutter create --org com.hayapenguin piyopiyo
```

### デフォルトの漢字フォントを変更する
デフォルトだと漢字が中華フォントで表示されてしまうので、日本語ローケルを設定する。

`MaterialApp`の`supportedLocales`プロパティに`Locale('ja')`を指定する。

```dart
import 'package:flutter_localizations/flutter_localizations.dart';

 return MaterialApp(
      debugShowCheckedModeBanner: false,
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [Locale('ja')],
      ...
    );
```

ref: https://flutter.dev/docs/development/accessibility-and-localization/internationalization

### 良くわからないエラーが出たらとりあえずHot Restart
思考停止再起動が上手くいくのと同じ感じのノリ。
おそらくHot reloadだとWidgetが変に残っててそれが悪さをしてエラーになる場合がある。

## Flutter設定(Android)

### デバッグ用の署名証明書 SHA-1, SHA-256を取得する
```bash
keytool -list -v \
-alias androiddebugkey -keystore ~/.android/debug.keystore
```
要求されるパスワードはデフォルトだと`android`

ref: https://developers.google.com/android/guides/client-auth


## Widgetの使い方
### Buttonをdisabledにする
`onPressed`プロパティに渡す関数をnullにする。
別途disabledプロパティ的なものがあれば嬉しいが、ないので三項演算子を使うことになる。

```dart
return ElevatedButton(
  child: const Text('ボタン'),
  onPressed: isValid ? doSomething : null,
)
```

ref: https://api.flutter.dev/flutter/material/TextButton-class.html
```
If the onPressed and onLongPress callbacks are null, then this button will be disabled, it will not react to touch.
```

### TextFieldに入力文字数を表示しない方法
TextFieldに`maxLength`プロパティを指定するとテキストの最大入力文字数を指定することができる。その場合、テキストフィールドの右下に現在の入力文字数が表示されるが、隠したいときの方法。

他の入力制限と組み合わせるも考えると`inputFormatters`を使うのが良いと思う。
`InputDecoratioin`の`counterText`プロパティに空文字を指定することもできるが、だったらmaxLengthプロパティを使うなって話だし、表示されないだけで表示領域を取ってしまうのでデザイン的にもなし。

```dart
return TextFormField(
  inputFormatters: [
    LengthLimitingTextInputFormatter(12),
  ],
  ...
)
```

### Rowの中のTextを折り返す
TextをExpandedで囲んであげる。

ref: https://github.com/flutter/flutter/issues/4128#issuecomment-584347376


ref: https://stackoverflow.com/questions/51893926/how-can-i-hide-letter-counter-from-bottom-of-textfield-in-flutter

### Appbarのテキストを真ん中表示にする

`centerTitle`プロパティを`true`にする。 Centerウィジェットを使う方法では中心からずれる。

ref: https://api.flutter.dev/flutter/material/AppBar/centerTitle.html

## Widgetの使い分け

### TextField vs TextFormField
入力値あをバリデートする必要があるならTextFormFieldを使うと良い。
良くわからなければとりあえずTextFieldを使って、それで機能的に不足する場合にTextFormFieldを使えば良い。

ref: https://stackoverflow.com/questions/53964219/flutter-enable-disable-button-based-on-textformfield-content

## パッケージ

### ピン入力のUIを作りたい
SMS認証時のピンコード入力のUIを作成する方法。
もちろん自力でもできるが、pin_input_text_fieldを使うのがおすすめ。

ref: https://pub.dev/packages/pin_input_text_field

### Riverpodの使い分け
pub.devにriverpod, flutter_riverpod, hooks_riverpodの3つがある。
Dartのみで使用する場合はriverpod、Flutter Hooksと使うならhooks_riverpodを使用する。

ref: https://riverpod.dev/docs/getting_started

### アプリのバージョンを取得したい
`package_info`パッケージを使用する。

ref: https://pub.dev/packages/package_info

## Firebase(全般)

### リージョンの指定
日本向けのサービスの場合は基本的には`asia-northeast1`(東京)でOK。
状況によっては`asia-northeast2`（大阪）でも良い。

東京リージョンなら基本的にGCPのほとんどの機能が使えるが、一応以下から使用予定の機能が使えるか確認しておくのが無難。
https://cloud.google.com/about/locations?hl=ja#asia-pacific

## Firebase Authentication

### SMS認証でピンコードを6桁から変えたい
2021年1月現在の仕様では不可能。
よくあるピンコードの長さは4桁だが、セキュリティ的には長い分には問題ないので敢えて4桁にする必要はない。

ref: https://stackoverflow.com/questions/46141010/can-i-customize-the-firebase-otp-digits-default-6-to-4

### SMS認証のテスト時に怪しいと怒られる

こんな感じのエラーメッセージ。

```
[firebase_auth/too-many-requests] We have blocked all requests from this device due to unusual activity
```

要は、リクエスト送りすぎ、お前怪しすぎということ。
とはいえ、認証周りの開発時にはどうしてもリクエストを何回も送る必要がある。
Firebaseのコンソール上からテスト用の電話番号と認証コードのセットが用意できるので、開発時はそちらを使用するのがベスト。

ref: https://stackoverflow.com/questions/37601942/firebase-3-we-have-blocked-all-requests-from-this-device-due-to-unusual-activi


### Android端末でSMS認証時にアプリが落ちる
Safetynetの設定がない場合、端末の認証のためにブラウザを開こうとするがブラウザがないとアプリが落ちてしまう。
`android/app/build.gradle`にブラウザを使用する設定を追加するのが`implementation "androidx.browser:browser:1.3.0"`1つの解決策。

ただ認証コード送信リクエスト時に一度ブラウザが立ち上がるというUX的にはクソな感じになってしまうので普通にSafety Netの設定をするのが無難。

FirebaseのプロジェクトにSHA-256フィンガープリントを追加するのと、GCPコンソールから`Android Device Verification`のAPIを有効にすると良い。

ref: https://stackoverflow.com/questions/64588281/caused-by-java-lang-classnotfoundexception-didnt-find-class-in-firebase-phone

## Firebase Hosting
### 1つのコードで複数のFirebase Hostingにデプロイしたい

ref: https://stackoverflow.com/questions/63524604/flutter-web-flavor
ref: https://firebase.googleblog.com/2016/07/deploy-to-multiple-environments-with.html

## Flutter Web
### Flutter Webかどうかを判別する
`kIsWeb`定数で判別できる。

ref: https://api.flutter.dev/flutter/foundation/kIsWeb-constant.html

### dart.htmlパッケージがAndroid(iOS)だとエラーになる

`dart.html`パッケージはWebにしか対応していないみたいで、アプリとしてビルドしようとすると`Error: Not found: 'dart:html'`と表示されてしまう。
解決策はいくつかあるが、一番簡単なのが、代わりに`universal_html`を使用すること。
そのままimportすると`flutter/material.dart`のWidgetと一部重複してエラーになってしまうので、以下のようにエイリアスをつけて読んであげる。

```dart
import 'package:universal_html/html.dart' as html;
```

ref: https://pub.dev/packages/universal_html

### Flutter WebでFirebase SMS認証時に出るreCAPTCHAのWidgetを消したい

2021年1月19日現在ではパッケージのバグでFlutter WebだとreCAPTCHAを非表示にすることができない。
回避策としては、直接DOM操作をしてreCAPTCHAの要素を非表示にする。


```dart
    final el = window.document.getElementById('__ff-recaptcha-container');
    if (el != null) {
      el.style.visibility = 'hidden';
    }
```


ref: https://github.com/FirebaseExtended/flutterfire/issues/4377


### Textの下部が見切れる
Flutter Web固有の不具合みたいです。一応回避策はあるものの根本的には対応を待つのみです。

ref: https://stackoverflow.com/questions/59672179/flutter-for-web-text-cut-off-at-bottom

### URLから#を取り除きたい
[url_strategy](https://pub.dev/packages/url_strategy)パッケージを利用する。

ref: https://stackoverflow.com/questions/59870357/how-to-remove-hash-from-url-in-flutter-web

## Cloud Functions
### Unauthenticatedエラーが出る
GCPのIAMからAllusersに対してCloud Functions Invokerのロールを付与する

firebase-firestore-https-callable-cloud-functions
 Unhandled Exception: [firebase_functions/unauthenticated] UNAUTHENTICATED

ref: https://stackoverflow.com/questions/62415509/unauthenticated-error-in-

## 未分類

### TextFieldのフォーカスを外す
https://qiita.com/tsukushibito/items/d7fde1d998a30292add7

### ListViewの中にListViewを入れる
https://qiita.com/code-cutlass/items/3a8b759056db1e8f7639

### freezedでReferenceをプロパティとして持たせたい
そのままだと変換がうまくできないのでJsonConverterを使用する。
ref: https://qiita.com/tetsufe/items/3c7d997f1c13c659628c#comment-5ce93b14ddfbc9ee6428

### EnumでString値を取得したい
extenstionを使用する。

ref: https://note.com/hatchoutschool/n/n3a7c6ae218fa

### KeyBoardが表示されたときにレンダリングエラーが起きる
ref: https://stackoverflow.com/questions/46551268/when-the-keyboard-appears-the-flutter-widgets-resize-how-to-prevent-this


## 未分類（書きかけ）



### いい感じのアーキテクチャ
https://github.com/bizz84/starter_architecture_flutter_firebase

### Firebaseを環境(Flavor)ごとにわける
https://qiita.com/hiromasa-fun/items/c79c99535f6f1db2a6a9