flutter build appbundle works but Play Console says the bundle is signed with the debug key. How do I create a keystore and configure Gradle without committing secrets?
DEV3 Developer Forum
How do I sign a Flutter Android release (keystore, key.properties) for the Play Store?
Every post is reviewed first. Usually reviewed within 24 hours.
1 reply
1. Create an upload keystore (once)
keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA \
-keysize 2048 -validity 10000 -alias upload
Store the file and passwords somewhere safe (password manager, backup). Outside the repository.
2. Reference it from android/key.properties
storePassword=YOUR_STORE_PASSWORD
keyPassword=YOUR_KEY_PASSWORD
keyAlias=upload
storeFile=/Users/you/upload-keystore.jks
Add key.properties, *.jks and *.keystore to .gitignore.
3. Configure android/app/build.gradle (Groovy)
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}
buildTypes {
release {
signingConfig signingConfigs.release
}
}
}
Newer Flutter templates use the Kotlin DSL (android/app/build.gradle.kts). The same setup there looks like this:
import java.io.FileInputStream
import java.util.Properties
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = keystoreProperties["storeFile"]?.let { file(it) }
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
}
}
}
4. Build
flutter build appbundle --release
The output is build/app/outputs/bundle/release/app-release.aab.
Play App Signing
Enroll in Play App Signing (default for new apps). Google holds the app signing key and your keystore is only the upload key, so a lost upload key can be reset through support instead of ending the app.
CI
Store the keystore base64-encoded as a secret, decode it in the pipeline and write key.properties there.
Pitfalls
- Wrong alias or password gives "Keystore was tampered with, or password was incorrect".
- Never lose the keystore of an app not using Play App Signing, you cannot publish updates without it.