自分用のメモです。
後々どうせやるけど面倒くさくなりそうな最低限のタスク達。
環境
スペック | |
---|---|
pc | macbook air |
メモリ | M2 |
OS | Sonama 14.2 |
flutter | 3.19.6 |
dart | 3.3.4 |
devtools | 2.31.1 |
xcode | 15.3 |
android studio | 2023.3.1 (Jellyfish) |
bundle identifier
プロジェクト作る時に
flutter create --org com.your.orgname my_app_name
で作るか、
vscodeのSetting.jsonで自動的に入力するようにしておくと楽。
↓参考
Flutterで作ったプロジェクトのBundleID・PckageNameを変更する方法と対策
画面の回転設定
Flutter でアプリの画面を Portrait 固定にするがすごい簡潔でよかった。
ios
Runner > General で設定。回転しないならportraitのみにする。
android
android/app/src/main/AndroidManifest.xmlのactibityタグにandroid:screenOrientation="portrait"
を追加する。
共通
スクリーンによる回転の可・不可、回転時のUIの切り替えは【Flutter】画面回転に対応するが参考になりそう(ちゃんと確かめてはない)
エミュレータで確認しておく
ios
1. open -a Simulator
でシミュレータを起動
2. flutter run
でアプリを起動
3. -dをつけると何で起動するか選べる
flutter devices
で使えるデバイスを調べる。
機種名の右側の文字列の先頭一文字を指定することで選べる。
iPhone 15 Pro Max (mobile) • 000000000000-000000-000000
だったらflutter run -d 0
android
初回(AVDがない)の場合
Start building Flutter Android apps on macOSに従って進めていく。
1. android studioを立ち上げる。
2. 「Get from VCS」の右隣の3点リーダーの「Virtual Device Manager」を選択。
3. +ボタンをクリック。(ホバーすると「Create Virtual Device」が表示される)
5. 任意のイメージを選択してダウンロードボタンをクリック。
少し待つとインストールが終わるので「Finish」をクリック。
6. インストールされたイメージを選択して「Next」
7. 細かい設定ができるが多分特に何もしなくていいので「Finish」
8. command-line toolsがない場合、android studioのsdk managerのsdk toolsから最新のものをインストールする。アコーディオンメニューのようになってるので、余分なものはしまわないと出てこない。
↓参考
[基礎知識]Android Studio(Cnary6ビルド(Jellyfish))インストール記録
1. エミュレータの一覧を取得
emulator -list-avds
※ $ANDROID_SDK_ROOT/emulator
にパスを通しておかないとできないので 注意。
2. emulator -avd {avd_name}
もしくはemulator @{avd_name}
で起動
emulator -avd pixel_8_pro
もしくは
emulator @avd pixel_8_pro
など
flutter_dotenv
flutter_dotenvを入れておく。
のちのちIDとか管理することになる。
以下公式のドキュメントの引用。
1. pub add
かpub get
で追加。
2. assetsに.env
を追加する。
assets:
- .env
3. プロジェクトのルートディレクトリに.envファイルを作る。
4. main.dart
でenvファイルをロードする。
(このステップ以降コードはv5.0.0以降のもの)
import 'package:flutter_dotenv/flutter_dotenv.dart';
// DotEnv dotenv = DotEnv() is automatically called during import.
// If you want to load multiple dotenv files or name your dotenv object differently, you can do the following and import the singleton into the relavant files:
// DotEnv another_dotenv = DotEnv()
Future main() async {
// To load the .env file contents into dotenv.
// NOTE: fileName defaults to .env and can be omitted in this case.
// Ensure that the filename corresponds to the path in step 1 and 2.
await dotenv.load(fileName: ".env");
//...runapp
}
5. 環境変数にアクセスする
import 'package:flutter_dotenv/flutter_dotenv.dart';
dotenv.env['VAR_NAME'];
アプリアイコン
任意のフォルダに画像を保存 e.g.) assets/images
assets:
- assets/
- assets/images/
- assets/images/launcher/
- assets/images/splash/
アイコンの設定を追記
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
# アイコンの設定
flutter_launcher_icons:
ios: true
image_path: "assets/images/launcher/icon.png"
remove_alpha_ios: true
android: true;
adaptive_icon_background: "assets/images/launcher/icon_android_background.png"
adaptive_icon_foreground: "assets/images/launcher/icon_android_foreground.png"
アイコンに使用する画像ファイルの名前には'-'は使えないので注意が必要。
flutter_launcher_icons
を実行する。
dart run flutter_launcher_icons
シミュレーターでアプリ走ってる場合は再起動orシミュレータからアプリを削除して再度run。
↓参考
【Flutter】アプリアイコンに必要な画像と追加方法(flutter_launcher_icons,Figma)
画像が見切れてるアイコンを生成したい時
iosではこのままの画像を用意するだけ。しかしandroidのadaptive iconは勝手に画像をマスクしにくるので工夫が必要。
何もせずにios用に用意した画像をそのまま使うと↓みたいな感じになる(pixel 8 proのエミュレーターで検証)。
以下解決策
1. andorid用に画像を用意。機種によってアイコンの形が変わり、それによってマスクの仕方が変わる(っぽい?)ので、余裕を持たせてios用のものよりも画像が見える範囲を少し広めにしておいた方がいいかもしれない。ただし、四角くマスクされる場合もあるので要検討。
(検証できてないけど108x108のサイズで作っておいた方が良さそう。)
2. 1で用意した画像をadaptive_icon_background
に割り当てる。そうすることでマスクされるのを回避する。
3. adaptive_icon_foreground
には透明なだけの透過画像を割り当てる。この画像は透明にする以外特に工夫は要らなそう。
3のforegroundの画像になんか書いたら遊び心があっていい感じのアイコンが作れるかもしれない。けどどっちにしろiosには適用できなさそう(?)
スプラッシュ画面
スプラッシュ画面作成
アイコン同様に画像を配置。
androidに対応するため画像は直径768pxの円に収まるものを用意しておくと楽
設定を追記。
# アイコンの設定
flutter_launcher_icons:
ios: true
image_path: "assets/images/launcher/icon.png"
remove_alpha_ios: true
android: true;
adaptive_icon_background: "assets/images/launcher/icon_android_background.png"
adaptive_icon_foreground: "assets/images/launcher/icon_android_foreground.png"
# スプラッシュ画面の設定
flutter_native_splash:
image: "assets/images/splash/splash.png"
color: "#f5f5f5"
android: true
ios: true
fullscreen: true
android_12:
image: "assets/images/splash/splash.png"
color: "#f5f5f5"
flutter_native_splash:create
を実行する。
dart run flutter_native_splash:create
アイコン同様再起動。
↓参考
【Flutter】コマンド一発でスプラッシュ画面を実装する【flutter_native_splash】
アプリ内での処理
main.dartのmain関数内にコードを追記。
スプラッシュ画面を正常に表示する。
void main() {
// 追記
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
// 追記
FlutterNativeSplash.preserve(widgetsBinding: widgetsBinding);
runApp(const MyApp());
}
任意の位置(initStateとか)にコードを追記。
データのロードなどの処理を行った後など適切なタイミングでスプラッシュ画面を終了する。
flutter_native_splashをフルスクリーンで表示すると、終了後ステータスバー(画面上部の時間とか表示する領域)が表示されなくなったりするので、表示する処理を記述する。
// スプラッシュ画面終了
FlutterNativeSplash.remove();
// splash screen 終了後にステータスバーを表示する
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual,
overlays: SystemUiOverlay.values);
↓の2つをインポートしなきゃいけない。
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:flutter/services.dart';
services.dart
はSystemChrome
を使うため。
環境切り替え
とにかく↓の記事通り進めていく。
↓のコマンドで実行(build
も)することで切り替わる。
flutter run --dart-define-from-file=path/filename
この設置をした後xcodeでビルドしようとすると、product bundle identifireがないと言われて失敗することがある。
ターミナルなどで一度--dart-define-from-file
をつけてビルドしなきゃいけない。
(product bundle identifierが、環境ファイルに書かれたもので上書きされる。)
firebase
firebase初めての時はcliをインストトール
Firebase CLI リファレンスに従って進める
1. mac or linuxなら
curl -sL https://firebase.tools | bash
npmを使ってるなら
npm install -g firebase-tools
でインストールするのが良さそう。
どちらにも当てはまらないならバイナリをダウンロードして頑張ってパスを通す。
2. 正しくインストールできたか確かめる。
firebase --version
3. firebaseにログインする
firebase login
firebaseのサイトに飛ばされるので、任意のアカウントでログイン。
プロジェクトを作る
firebaseにログインして、「プロジェクトを追加」から作成。
プロジェクト名を入力して進む。
プロジェクトIDは重複がある場合適当に割り振られるorいい感じのものをつける。
プロジェクトは環境の数だけ作る。dev, stg, prdがあるなら、appname-dev, appname-stg, appname-prdを作る。
それぞれのプロジェクトで、androidとiosのアプリを作る。
flutterにfirebaseを追加
android/app/src/
とios/
に環境分のディレクトリを作って、firebase sdkの構成ファイルを配置する。
androidとiosでそれぞれ環境に応じて構成ファイルを参照できるようにする。
を参考にする。
というか完全に↓(同記事)を参考にしたらいい。
flutterにfirebaseを使うためのプラグインを追加する。
flutter pub add firebase_core
この段階でiosのビルドをしようとすると、pod installしろと言われる場合がある。
Specs satisfying the `firebase_core (from `.symlinks/plugins/firebase_core/ios`)` dependency were found, but they required a higher minimum deployment target.
と言われた場合はビルドターゲットのバージョンが低い。firebaseは2024/7/24時点では13以上が必要っぽい。ios/Podfileの先頭に↓を追記する。
platform :ios, '13.0'
podを更新する
pod install --repo-update
flutterfire_cliの追加
dart pub global activate flutterfire_cli
(※flutter pub
とは異なるコマンド)
↓参考
追加しただけではflutterfire
コマンドは使えないので注意。
コンソールに出てくるexport PATH="$PATH":"$HOME/.pub-cache/bin"
でパスを通す。
環境毎にflutterfire configureする
次項のfirebaseの初期化に用いられるfirebase_options
が、環境によって適切に使われるようにしたい。
例えば開発環境なら、firebase_options_dev.dart
を使う。
flutterfire configure
で毎にファイルを生成する。この時生成されるfirebase_options
ファイルは特に書き換えたりしない。
project idはfirebase projects:list
で確認できる。
# dev環境
flutterfire configure --project=<firebase-dev-project-id> --out=lib/firebase_options_dev.dart
# stg環境
flutterfire configure --project=<firebase-stg-project-id> --out=lib/firebase_options_stg.dart
# prd環境
flutterfire configure --project=<firebase-prd-project-id> --out=lib/firebase_options_prd.dart
firebase.json
を編集して環境毎のアプリidを記述する。
プロジェクトidはflutterfire configure
の時のものと同じ。アプリidは、firebaseのコンソールから「プロジェクトの設定」を開き、下の方の「マイアプリ」のところに書いてある「アプリID」をコピペする。
"default"はどの環境を書いても(多分)大差ないので適当に書いておく。
{
"flutter": {
"platforms": {
"android": {
"default": {
"projectId": "{プロジェクトid}",
"appId": "{アプリid}",
"fileOutput": "android/app/google-services.json"
}
},
"ios": {
"default": {
"projectId": "{プロジェクトid}",
"appId": "{アプリid}",
"uploadDebugSymbols": false,
"fileOutput": "ios/Runner/GoogleService-Info.plist"
}
},
"dart": {
"lib/firebase_options_dev.dart": {
"projectId": "{プロジェクトid}",
"configurations": {
"android": "{アプリid}",
"ios": "{アプリid}"
}
},
"lib/firebase_options_stg.dart": {
"projectId": "{プロジェクトid}",
"configurations": {
"android": "{アプリid}",
"ios": "{アプリid}"
}
},
"lib/firebase_options_prd.dart": {
"projectId": "{プロジェクトid}",
"configurations": {
"android": "{アプリid}",
"ios": "{アプリid}"
}
}
}
}
}
}
firebaseのapiキーは公開されても大丈夫らしい。
firebaeを初期化
firebase_core
とそれぞれのfirebase_opsions
をインポート。使い分けられるように
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options_dev.dart' as dev_default_firebase_options;
import 'firebase_options_stg.dart' as stg_default_firebase_options;
import 'firebase_options_prd.dart' as prd_default_firebase_options;
WidgetsFlutterBinding.ensureInitialized();
の後にFirebase.initializeApp
を追加して初期化する。この時環境毎に適切なfirebase_options
を引数に渡す。
WidgetsBinding widgetsBinding = WidgetsFlutterBinding.ensureInitialized();
const flavor = String.fromEnvironment('flavor');
FirebaseOptions options;
switch (flavor) {
case "prd":
options =
prd_default_firebase_options.DefaultFirebaseOptions.currentPlatform;
break;
case "stg":
options =
stg_default_firebase_options.DefaultFirebaseOptions.currentPlatform;
break;
default:
options =
dev_default_firebase_options.DefaultFirebaseOptions.currentPlatform;
}
// firebaseを初期化
await Firebase.initializeApp(
options: options,
);
chatGPTが教えてくれたので、これがいい方法なのかはわからない。今度リサーチしたい。
flutterに機能を追加
pubspec.yamlに使いたいfirebaseの機能を追加。
dependencies:
flutter:
sdk: flutter
# 省略
firebase_core: ^2.31.0
firebase_analytics:
firebase_crashlytics:
認証を使いたいならfirebase_auth
など。
admob(広告)
この記事ではバナー広告だけ扱う。
info.plistなどに設定の記述がないままpubspec.yamlに追加して実行(コンパイル)しようとするとエラーが出て失敗するので、後で追加するか一旦コメントアウトしておくといい
admobに登録する必要があるが簡単なので割愛。
手順は公式のFlutter アプリに AdMob 広告を追加するで確認するのが良さそう。ただし、最終更新日が2022年なのはちょっと不安。
admobで広告を作成
これも簡単な作業なので一旦割愛。
firebaseとリンク
admobの設定画面の「アプリリンクの管理」からリンク
すでにfirebaseでプロジェクトを作ってる場合は、identifierを入力すると該当のプロジェクトが出てくる。
IDを記述
設定ファイルに色々記述がないと表示ができない。というかそもそもコンパイルできない。
開発時は動作確認用にテスト用の広告を使う。
本番の広告の確認をするにはテストデバイスの登録をする必要がある。テスト広告を有効にするを参考にするといい。
テスト用のアプリIDと広告ユニットIDは3. AdMob アプリと広告ユニットを設定するに書かれている↓の表から持ってくる。
ios
ios/Runner/Info.plistを編集
GADApplicationIdentifier
にアプリIDを割り当てる。
(↓のコードはテスト用ID)
...
<key>GADApplicationIdentifier</key>
<string>ca-app-pub-3940256099942544~1458002511</string>
...
テスト用アプリID
ca-app-pub-3940256099942544~1458002511
テスト用広告ユニットID(バナー)
ca-app-pub-3940256099942544/2934735716
android
android/app/src/main/AndroidManifest.xmlを編集
ios同様アプリIDを割り当てる。
(↓のコードはテスト用ID)
<manifest>
...
<application>
...
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713"/>
</application>
</manifest>
テスト用アプリID
ca-app-pub-3940256099942544~3347511713
テスト用広告ユニットID(バナー)
ca-app-pub-3940256099942544/6300978111
ユニットID
ad_helper.dartにユニットIDを記述しておく。(名前はなんでもいい)
デバッグビルドとリリースビルドで返すIDを切り替えるようにしておくと便利。
import 'dart:io';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class AdHelper {
static String get BanneradUnitId {
bool isDebug = false;
// debug時のみ実行
assert(isDebug = true);
if (isDebug) {
// デバッグ時
if (Platform.isAndroid) {
return "a-app-pub-3940256099942544/6300978111";
} else if (Platform.isIOS) {
return "ca-app-pub-3940256099942544/2934735716";
} else {
throw UnsupportedError("Unsupported platform");
}
} else {
// リリース時
if (Platform.isAndroid) {
return dotenv.env['ANDROID_UNIT_ID']!;
} else if (Platform.isIOS) {
return dotenv.env['OS_UNIT_ID']!;
} else {
throw UnsupportedError("Unsupported platform");
}
}
}
}
広告のロード
広告を表示するwidgetを作って、initState内で広告をロードする。
ロードが成功した時のみバAdWidgetを生成する。
使われなくなったバナーはdispose()
でリリースする。
@override
void dispose() {
adState.banner.dispose();
super.dispose();
}
import 'package:flutter/material.dart';
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'package:path/to/ad_helper.dart';
class BannerAdContainer extends StatefulWidget {
const BannerAdContainer({super.key});
@override
State<BannerAdContainer> createState() => _BannerAdContainerState();
}
class _BannerAdContainerState extends State<BannerAdContainer> {
BannerAd? banner;
@override
void initState() {
super.initState();
BannerAd(
size: AdSize.banner,
adUnitId: AdHelper.BanneradUnitId,
listener: BannerAdListener(
onAdLoaded: (ad) {
debugPrint('Ad loaded: ${ad.adUnitId}');
setState(() {
banner = ad as BannerAd;
});
},
onAdClosed: (ad) {
debugPrint('Ad closed: ${ad.adUnitId}');
},
onAdFailedToLoad: (ad, error) {
debugPrint('Ad failed to load: ${ad.adUnitId}, $error');
ad.dispose();
},
onAdOpened: (ad) {
debugPrint('Ad opened: ${ad.adUnitId}');
},
onAdClicked: (ad) {
debugPrint('Ad clicked: ${ad.adUnitId}');
},
onAdImpression: (ad) {
debugPrint('Ad impressed: ${ad.adUnitId}');
},
onAdWillDismissScreen: (ad) {
debugPrint('Ad screen dismissed: ${ad.adUnitId}');
},
),
request: const AdRequest(),
).load();
}
@override
void dispose() {
banner?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.grey,
height: 100,
child: Center(
child: banner != null
? AdWidget(ad: banner!)
: const Text(
'広告',
style: TextStyle(color: Colors.white),
),
),
);
}
}
google mobile adsを初期化
広告をロードする前の適当なところで初期化処理を行う。
とりあえずmainでやっておけばいい。
import 'package:google_mobile_ads/google_mobile_ads.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
MobileAds.instance.initialize();
runApp(MyApp());
}
広告を表示
適当なところに表示する。バナーを画面上部か下部に表示するならColumn使っとく。
body: Column(
children: [
Expanded(
// 省略
),
const BannerAdContainer(),
],
),
テストデバイスの設定
テストデバイスを設定して安全に本番広告を配信する。
admobにログインして、設定 > テストデバイスからテストデバイスを追加する。
↓参考
app idを切り替える
環境切り替えの要領で切り替える。
dev.json, stg.json, prd.jsonにそれぞれapp idを追加する。
prd.jsonだけ(場合によってはstg.jsonも)idを本番用idにする。
(↓はテスト用app id)
{
// 省略
"admobAppIdIos": "ca-app-pub-3940256099942544~1458002511",
"admobAppIdAndroid": "ca-app-pub-3940256099942544~3347511713"
}
prd.jsonは必ず.gitignoreに追加する!!!!
先にprd.jsonがpushされている場合などはgitignore後で追加した時の方法で削除する。
本当はprd.jsonに直接idを書きたくなかったけど、これ以外のいい方法がわからなかったので何か他にあったら知りたい。
ios
info.plistのGADApplicationIdentifier
の値を↓のように変更する。
<key>GADApplicationIdentifier</key>
<string>$(admobAppIdIos)</string>
</dict>
</plist>
これでiosのapp idが切り替わる
android
android/app/build.gradleのdefaultConfig
にresValue
を追加する。
defaultConfig {
// 省略
applicationId "${dartDefines.appId}"
resValue "string", "app_name", "${dartDefines.appName}"
// ↓追加
resValue "string", "admob_app_id_android", "${dartDefines.admobAppIdAndroid}"
}
android/app/src/main/AndroidManifest.xmlのcom.google.android.gms.ads.APPLICATION_ID
の値を@string/admob_app_id_android
に書き換える。
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="@string/admob_app_id_android"/>
</application>
これでandroidのapp idが切り替わる。
実機デバイス用の広告リクエス設定
このままではデバイスIDを設定したスマホでテストモードの本番広告(正式な名称がわからない)を表示することが出来ない。
test device identifier(デバイスIDをハッシュ化したもの?)をコード上で設定してやらなければいけない。
(xcodeでログを見た時に初めて気づいたので、実機で広告を表示するのに随分時間がかかった。)
<Google> To get test ads on this device, set:
Objective-C
GADMobileAds.sharedInstance.requestConfiguration.testDeviceIdentifiers = @[ @"00000000000000000000000000000" ];
Swift
GADMobileAds.sharedInstance().requestConfiguration.testDeviceIdentifiers = [ "000000000000000000000000000000" ]
デバイスIDを取得する
import 'package:device_info_plus/device_info_plus.dart';
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
String? testDeviceId;
if (Platform.isIOS) {
IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
testDeviceId = iosInfo.identifierForVendor;
} else if (Platform.isAndroid) {
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
testDeviceId = androidInfo.id;
}
debugPrint('Test Device ID: $testDeviceId');
identifierを設定する
// デバイスIDをMD5でハッシュ化
var bytes = utf8.encode(testDeviceId);
var digest = md5.convert(bytes);
var id = digest.toString();
RequestConfiguration requestConfiguration = RequestConfiguration(
testDeviceIds: [id],
);
await MobileAds.instance
.updateRequestConfiguration(requestConfiguration);
これだけだと、設定が終わる前にロードしてしまって失敗するので、非同期に制御してあげた方がいい。
ステータスバーの色を変更する(android)
(特にこだわりが無ければやらなくてもいい。けどiosとデザイン変わってしまうのでやった方がいいかも。)
androidは自分でステータスバーの色を変えてあげなきゃいけない(っぽい)
Platform
を使うためdart:io
をインポート。
import 'dart:io';
// 省略
void main() {
// 省略
if (Platform.isAndroid) {
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.white, // ステータスバーの背景色を設定
));
}
runApp(const MyApp());
}
番外編
gitignore後で追加した時
.gitignoreにignoreしたいものを追加する。
git rm --cached ファイル名
or
git rm --cached -r ディレクトリ名
で削除。
commit & push するとリモートからgitignoreしたいものが消える。
↓参考
admobログイン
なぜかarcではページを読み込めないのでchromeとかでログインする。
(↑これマジでなんで?)