跳过正文

Flutter 保姆级上架 Google Play 教程

·3 分钟· loading · loading ·
lixxix
作者
lixxix
关注科技,编程改变生活!
把 Flutter 应用上架 Google Play,核心在于用上传密钥(upload keystore)对 release 包进行签名。本文梳理了从生成 keystore、配置 Gradle 自动签名,到打包 AAB 发布包的完整流程,照着做即可。

Step 1:生成 keystore 签名文件
#

在项目根目录执行 keytool 生成上传密钥,输出到 android/app/upload-keystore.jks

keytool -genkey -v \
  -keystore android/app/upload-keystore.jks \
  -keyalg RSA \
  -keysize 2048 \
  -validity 10000 \
  -alias upload \
  -dname "CN=OvoGame, OU=Dev, O=OvoGame, L=Beijing, ST=Beijing, C=CN"

由于 -dname 已经把身份信息(CNOL 等)通过命令行直接传入,keytool 只会询问密码:

Enter keystore password:    ← 输入 keystore 密码(自己设一个,务必记住)
Re-enter new password:      ← 再输一次确认
Enter key password for <upload>:
        (RETURN if same as keystore password):   ← 直接回车,表示与 keystore 密码相同

-dname 中的公司/地区信息和 -alias(别名,后续 key.properties 要用到)都可以按需修改,但要保证三处配置里的别名一致。

务必妥善保管 keystore 文件和密码。Google Play 的应用签名一旦绑定该密钥,后续所有版本更新都必须用同一个密钥签名,丢失将无法继续更新应用。

Step 2:创建 key.properties
#

这个文件的作用是把 keystore 的密码告诉 Gradle,这样打包时能自动完成签名。它包含明文密码,不能提交到 git——好在 Flutter 默认的 android/.gitignore 里已经包含了 key.properties(以及 **/*.jks)这两条规则,密钥文件天然安全。

android/ 目录下创建 key.properties,内容如下(替换为你的实际密码):

storePassword=********
keyPassword=********
keyAlias=upload
storeFile=upload-keystore.jks
字段含义
storePasswordkeystore 文件的密码
keyPassword别名为 upload 的密钥密码(上一步若直接回车,则与 storePassword 相同)
keyAlias生成 keystore 时指定的 -alias,必须一致
storeFilekeystore 文件路径,相对 android/app/ 解析

Step 3:配置 build.gradle.kts 启用 release 签名
#

打开 android/app/build.gradle.kts,做三处修改。

① 在文件顶部引入签名所需的两个类:

import java.util.Properties
import java.io.FileInputStream

② 在 plugins {}android {} 之间插入加载 key.properties 的逻辑:

// 从 android/key.properties 读取上传密钥凭据
// key.properties 已被 git 忽略,签名密钥不会进入版本控制
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}

③ 在 android {} 中新增 signingConfigs,并让 release 构建类型使用它。 完整的 android {} 配置如下:

android {
    namespace = "com.ovogame.app"
    compileSdk = flutter.compileSdkVersion
    ndkVersion = flutter.ndkVersion

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }

    signingConfigs {
        create("release") {
            keyAlias = keystoreProperties["keyAlias"] as String?
            keyPassword = keystoreProperties["keyPassword"] as String?
            storeFile = keystoreProperties["storeFile"]?.let { file(it as String) }
            storePassword = keystoreProperties["storePassword"] as String?
        }
    }

    defaultConfig {
        // TODO: Specify your own unique Application ID
        applicationId = "com.ovogame.app"
        minSdk = flutter.minSdkVersion
        targetSdk = flutter.targetSdkVersion
        versionCode = flutter.versionCode
        versionName = flutter.versionName
    }

    buildTypes {
        release {
            // 用 upload keystore (key.properties) 签名 release 构建
            signingConfig = signingConfigs.getByName("release")
        }
    }
}

namespaceapplicationId 要改成你自己的包名(如 com.yourcompany.yourapp),三个值需保持一致。

⚠️ 包名一旦上传过 Google Play 就永久锁定、不可更改。改包名只在「从未上架」时可行,否则会被识别成一个全新的应用。


Step 4:打包发布包
#

更新版本号
#

发版前先确认 pubspec.yaml 中的版本号:

version: 1.0.0+1
#          ↑   ↑
#     versionName versionCode
  • versionName1.0.0):用户可见的版本号
  • versionCode1):整数,每次发版必须递增(123),Google Play 据此判断新旧版本,上传时必须大于上一次

例如版本号演进:1.0.0+11.0.1+21.1.0+3

执行打包命令
#

签名配置完成后,执行构建命令生成发布包。Google Play 要求使用 AAB 格式:

flutter build appbundle --release

产物位于 build/app/outputs/bundle/release/app-release.aab

遇到缓存导致的异常时,可先 flutter clean && flutter pub get 清理旧产物再重新打包。

AAB 与 APK 的区别:

格式命令产物路径用途上架 Google Play
AAB (.aab)flutter build appbundle --releasebuild/app/outputs/bundle/release/app-release.aab官方推荐格式,按用户设备生成优化后的 APK✅ 必需
APK (.apk)flutter build apk --releasebuild/app/outputs/flutter-apk/app-release.apk自行分发(官网下载、第三方商店、真机测试)❌ 2021 年起不再接受

Step 5:验证产物
#

打完包别急着上传,先确认签名正确、能在真机运行。

真机安装测试
#

AAB 无法直接安装,先打一个 APK 用于真机验证:

flutter build apk --release
adb install build/app/outputs/flutter-apk/app-release.apk

确认能正常打开、WebView 加载正常、网络权限 OK。

验证签名
#

确认 release 包用的是你的 upload keystore、而非 debug 签名——对比 keystore 与 APK 的 SHA-256 指纹:

# keystore 的指纹
keytool -list -v -keystore android/app/upload-keystore.jks -alias upload -storepass <密码> | grep SHA256

# APK 实际签名指纹(需 Android SDK build-tools 的 apksigner)
apksigner verify --print-certs build/app/outputs/flutter-apk/app-release.apk | grep SHA-256

两个指纹一致,即证明发布包由你的上传密钥签名。


总结
#

整个上架前的签名配置流程可归纳为四步:

  1. keytool 生成 upload-keystore.jks
  2. android/key.properties 写入密钥凭据
  3. 修改 build.gradle.kts,让 release 构建自动签名
  4. flutter build appbundle 产出 AAB,上传到 Play Console

上传到 Google Play
#

拿到 app-release.aab 后,登录 Google Play Console 创建应用,依次完成商品详情填写、内容分级问卷与隐私政策,再上传 AAB 提交审核。几个关键点:

  • 首次需创建开发者账号,一次性费用 $25
  • 上传的必须是 .aab,而非 APK
  • 每次上传的 versionCode 必须大于上一次
  • 首次审核约 1-3 天,后续更新通常数小时

常见问题
#

Q:提示找不到 keytool / apksigner

直接用 Android Studio 自带的工具:

  • keytoolC:\Program Files\Android\Android Studio\jbr\bin\
  • apksigner<Android SDK>\build-tools\<版本>\(SDK 路径见 android/local.propertiessdk.dir

Q:打包失败提示签名相关错误?

依次检查:android/key.properties 是否存在且四项齐全;密码、alias 是否与创建 keystore 时一致;storeFile 路径相对 android/app/ 解析。

Q:构建很慢,或日志里有 tree-shaken 字样?

首次构建需下载并编译依赖,耗时几分钟正常,后续有缓存会快很多;遇到缓存异常时 flutter clean 后重试。构建日志中的 MaterialIcons-Regular.otf was tree-shaken 是正常优化(字体从 1.6MB 降到约 1KB),无需处理。