Flutter de アプリ開発〜リリース前にすること〜（Android編）

Flutterでアプリの第2弾リリースしてみました。
リリースしたアプリはこちら。今回はAndroid編ということでAndroidのリンクを。

https://play.google.com/store/apps/details?id=jp.geek_otter.butsuyoku_chokin

レコーディングダイエットならぬ、レコーディング物欲です。
今月はこれだけ無駄使いを我慢したんだな〜ということがわかるアプリです。
今回はアニメーションを実装してみました。
保存ボタンを押すと上からお金が降ってきます。
![ezgif.com-optimize.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/548237/6624b13a-71d6-be9b-077e-c664c48a099d.gif)
この記事では備忘録的に、意外と時間のかかるリリース前作業についてまとめたものです。
※2023年10月時点

## リリース前にしたこと
### スクリーン上のDEBUGの表示を消す

### Bundle IDのcom.exampleを変更
デフォルトだとBundle IDがcom.example〜となっているので、
プロジェクト内でgrepして適切なものへ変更。

### keystoreファイルを作成する
アプリへの署名のためのkeystoreファイルを作成する。
有効期限が短いとapp bundleのアップロード時に弾かれてしまうので長めに設定。

~~~
keytool -genkey -v -keystore my-release-key.jks -keyalg RSA -keysize 2048 -validity 36500 -alias mykey
~~~
~~~
キーストアのパスワードを入力してください:  
新規パスワードを再入力してください: 
姓名は何ですか。
  [Unknown]:  
組織単位名は何ですか。
  [Unknown]:  
組織名は何ですか。
  [Unknown]:  
都市名または地域名は何ですか。
  [Unknown]:  
都道府県名または州名は何ですか。
  [Unknown]:  
この単位に該当する2文字の国コードは何ですか。
  [Unknown]:  JP
CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=JPでよろしいですか。
  [いいえ]:  はい
~~~
国コードだけでも申請は通る。
key.jksはandroid/appへ格納する。

### android/key.propertiesを作成
~~~
storePassword=<パスワード>
keyPassword=<パスワード>
keyAlias=key
storeFile=key.jks
~~~
storePasswordとkeyPasswordにはkeystoreで設定したパスワードを入力する。
keyAliasは、以下を実行するとわかる。
~~~
keytool -list -v -keystore /YourProjectPath/key.jks
~~~

### build.gradleを編集してkey情報を認識させる
- android/app/build.gradle
~~~
plugins {
    id "com.android.application"
    id "kotlin-android"
    id "dev.flutter.flutter-gradle-plugin"
}

def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
    localPropertiesFile.withReader('UTF-8') { reader ->
        localProperties.load(reader)
    }
}

def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
    flutterVersionCode = '1'
}

def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
    flutterVersionName = '1.0'
}

// ★追記
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    namespace "jp.geek_otter.butsuyoku_chokin"
    compileSdkVersion flutter.compileSdkVersion
    ndkVersion flutter.ndkVersion

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = '1.8'
    }

    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'
    }

    defaultConfig {
        minSdkVersion flutter.minSdkVersion
        targetSdkVersion flutter.targetSdkVersion
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
    // ★追記
    signingConfigs {
        release {
            keyAlias keystoreProperties['keyAlias']
            keyPassword keystoreProperties['keyPassword']
            storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
            storePassword keystoreProperties['storePassword']
        }
    }

    buildTypes {
        release {
            // ★debug→releaseへ変更
            signingConfig signingConfigs.release
        }
    }
}

flutter {
    source '../..'
}

dependencies {}

~~~
### targetSDKの更新（必要に応じて）
app/bundle.gradle
~~~
    defaultConfig {
        minSdkVersion 26 // ここ
        targetSdkVersion 34 // ここ
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
~~~

### リリースビルドの実行
~~~
flutter build appbundle
~~~