demo 地址: https://github.com/iotjin/jh_flutter_demo
代码不定时更新,请前往github查看最新代码
有时为了方便测试,同一个app需要在一个手机上装两个,直接改包名的话比较麻烦,这时可以通过添加flavor进行多维度打包,同一份代码可以设置不同的包名,app应用名称和应用图标
效果图
flutter 一键打出不同包名、应用名、版本名、签名、应用图标、版本号的安装包
Android Studio 一个工程打包多个不同包名的APK
Android Gradle —— flavorDimensions 与 productFlavors
配置
需要修改的文件有两个,一个是
android/app/build.gradle
,一个是android/app/src/main/AndroidManifest.xml
productFlavors 配置
核心代码是在
build.gradle
添加 productFlavors 配置
flavorDimensions("default") // 定义一个维度,这个维度的名字叫default,和下面的productFlavors中的dimension "default"对应
productFlavors {
// app1 | dev
app1 {
dimension "default"
// 设置applicationId(这里很重要,两个相同applicationId的apk不能同时安装在同一台Android手机中)
// applicationId "com.jh.demo1"
applicationId "${defaultConfig.applicationId}.dev"
// 自动生成@string/app_name为demo 把AndroidManifest.xml中的 android:label="demo"替换成 android:label="@string/app_name"
// resValue "string", "app_name", "jh-demo1"
// 定义app_icon字段,在AndroidManifest.xml文件中用到
manifestPlaceholders = [
app_name: "jh-demo1",
app_icon: "@mipmap/ic_launcher",
]
}
// app2 | prod
app2 {
dimension "default"
// applicationId "com.jh.demo2"
applicationId "${defaultConfig.applicationId}"
// resValue "string", "app_name", "jh-demo2"
manifestPlaceholders = [
app_name: "jh-demo2",
app_icon: "@mipmap/ic_launcher",
]
}
}
然后需要在 AndroidManifest.xml
把 android:icon
和android:label
换成上面配置的app_name
和app_icon
替换前:
<application
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:label="jh_flutter_demo">
替换后:
<application
android:name="${applicationName}"
android:icon="${app_icon}"
android:label="${app_name}">
新的编译和打包命令
加入flavor 后,按原来的方式调试运行或者打包会报错
编译:
flutter run --flavor app1 -t lib/main.dart
打包:
flutter build apk --flavor app1
添加flavor后编译报错
需要在 编辑器顶部 Run/Debug Configuration 里面 build flavors 设置对应的flavor 如 app1
编译运行命令
flutter run --flavor app1 -t lib/main.dart
打包命令
flutter build apk --flavor app1 --release
flutter build apk --release --flavor app1 --target-platform=android-arm64
清除build缓存并且打包app1和app2的debug和release包
flutter clean; flutter build apk --flavor app1 --debug; flutter build apk --flavor app1 --release; flutter build apk --flavor app2 --debug; flutter build apk --flavor app2 --release
查看包名
aapt dump badging D:\apk\xxx.apk | findstr package
package: name='com.jh.jh_flutter_demo.dev' versionCode='6' versionName='3.16.0'
aapt dump badging D:\apk\xxx.apk
安裝apk
adb install -r D:\apk\xxx.apk
点击run 按钮运行需要配置下图:
打包报错 可以添加下面代码
lintOptions {
//在打包Release版本的时候不进行检测
checkReleaseBuilds false
// 有报错也不会停止打包
abortOnError false
// 防止报错:Error: The resource string/app_name has not been translated
disable 'InvalidPackage'
}
// https://github.com/flutter/flutter/issues/58247
// https://issuetracker.google.com/issues/158753935
完整build.gradle代码
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('app/key/key.properties') // 上面放置的路径
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}
android {
compileSdkVersion 33
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.jh.jh_flutter_demo"
minSdkVersion 21
targetSdkVersion 33
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
multiDexEnabled true
ndk {
abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86_64' // 'armeabi','x86'
}
}
signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
// v1SigningEnabled true
// v2SigningEnabled true
}
}
buildTypes {
// debug {
// signingConfig signingConfigs.release
// }
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.release
}
}
/*
一次打包不同的apk
添加flavor后编译报错
需要在 编辑器顶部 Run/Debug Configuration 里面 build flavors 设置对应的flavor 如 app1
编译运行命令
flutter run --flavor app1 -t lib/main.dart
打包命令
flutter build apk --flavor app1 --release
flutter build apk --release --flavor app1 --target-platform=android-arm64
清除build缓存并且打包app1和app2的debug和release包
flutter clean; flutter build apk --flavor app1 --debug; flutter build apk --flavor app1 --release; flutter build apk --flavor app2 --debug; flutter build apk --flavor app2 --release
查看包名
aapt dump badging D:\apk\xxx.apk | findstr package
package: name='com.jh.jh_flutter_demo.dev' versionCode='6' versionName='3.16.0'
aapt dump badging D:\apk\xxx.apk
安裝apk
adb install -r D:\apk\xxx.apk
*/
flavorDimensions("default") // 定义一个维度,这个维度的名字叫default,和下面的productFlavors中的dimension "default"对应
productFlavors {
// app1 | dev
app1 {
dimension "default"
// 设置applicationId(这里很重要,两个相同applicationId的apk不能同时安装在同一台Android手机中)
// applicationId "com.jh.demo1"
applicationId "${defaultConfig.applicationId}.dev"
// 自动生成@string/app_name为demo 把AndroidManifest.xml中的 android:label="demo"替换成 android:label="@string/app_name"
// resValue "string", "app_name", "jh-demo1"
// 定义app_icon字段,在AndroidManifest.xml文件中用到
manifestPlaceholders = [
app_name: "jh-demo1",
app_icon: "@mipmap/ic_launcher",
]
}
// app2 | prod
app2 {
dimension "default"
// applicationId "com.jh.demo2"
applicationId "${defaultConfig.applicationId}"
// resValue "string", "app_name", "jh-demo2"
manifestPlaceholders = [
app_name: "jh-demo2",
app_icon: "@mipmap/ic_launcher",
]
}
}
// lintOptions {
// //在打包Release版本的时候不进行检测
// checkReleaseBuilds false
// // 有报错也不会停止打包
// abortOnError false
// // 防止报错:Error: The resource string/app_name has not been translated
// disable 'InvalidPackage'
// }
// https://github.com/flutter/flutter/issues/58247
// https://issuetracker.google.com/issues/158753935
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}