为了方便可以使用三方库 https://pub-web.flutter-io.cn/packages/dart_definedart_define来自动生成配置文件。 https://pub-web.flutter-io.cn/packages/dart_define
一次配置,后边运行,只在需要在androidstudio 选择run不同的自定义配置就行了。
1:在 pubspec.yaml文件中配置
html
##### 渠道配置
dart_define:
# 生成Dart常量的配置
dart: true
json: true
dart_path: lib/app_config.gen.dart
class_name: AppConfig
# 定义所有变量(两个App差异化的部分)
variables:
- name: APP_NAME
description: 应用名称
required: true
- name: APP_ID # 关键:用于不同包名
description: 应用包名 (Application ID)
required: true
- name: DEBUG_API # 关键:测试地址
description: API测试地址
required: true
- name: RELEASE_API # 关键:正式地址
description: API正式地址
required: true
- name: ASSETS_BASE # 关键:用于不同Logo/图片路径
description: 资源文件基础路径
required: false
default: 'assets/' # 默认路径
# 定义两个定制风味(Flavors)
flavors:
- name: app_main
description: appOne
- name: app_ks
description: appTwo
2:执行命令
html
dart run dart_define generate \ --json_path=config/app_main.json \ --FLAVOR=app_main \ --APP_NAME="appOne" \ --APP_ID="com.package.app.edu" \ --DEBUG_API="https://api.edu.example.com" --RELEASE_API="https://api.edu.example.com" \ --ASSETS_BASE="assets/app_main/"
flutter run --dart-define-from-file=config/app_main.json
执行完后 在项目的根目录有一个config文件夹,里边可以配置多个app,看自己配置,如下:
html
config
| -- app_main.json
| -- app_ks.json
里边内容
{
"APP_NAME": "APP名字",
"APP_ID": "com.example.mom",
"DEBUG_API": "http://xxxxx:80/",
"RELEASE_API": "http://xxxxxx/",
"ASSETS_BASE": "assets/app_main/",
"FLAVOR": "app_main"
}
lib文件夹下会有一个app_config.gen.dart
html
class AppConfig {
AppConfig._();
/// The flavor the app was launched with
static Flavor get flavor => FlavorExtension.fromString(
const String.fromEnvironment('FLAVOR'),
);
/// 应用名称
static const appName = String.fromEnvironment('APP_NAME');
/// 应用包名 (Application ID)
static const appId = String.fromEnvironment('APP_ID');
/// API测试地址
static const debugApi = String.fromEnvironment('DEBUG_API');
/// API正式地址
static const releaseApi = String.fromEnvironment('RELEASE_API');
/// 资源文件基础路径
static const assetsBase = String.fromEnvironment('ASSETS_BASE');
}
/// The flavors supported by the application
enum Flavor {
/// 主版本
appMain,
/// ----
appKs,
}
extension FlavorExtension on Flavor {
static Flavor fromString(String value) {
switch (value) {
case 'app_main':
return Flavor.appMain;
case 'app_ks':
return Flavor.appKs;
default:
throw throw Exception('Invalid flavor');
}
}
}
如上dart文件已经配置完了,如果在项目中在不同的app中使用不同的图标。
图标配置路径是"ASSETS_BASE": "assets/app_main/",配置的,根据json配置文件去读取不同的资源
// 动态加载对应Flavor的Logo图片
Image.asset('${AppConfig.assetsBase}/logo.png'),
3:如上已经配置好dart文件,接下来配置android目录下的 build.gradle.kts中配置
在android{}之前增加如下
html
// 1. 定义存储解析后变量的Map
val dartEnvironmentVariables = mutableMapOf<String, String>()
// 2. 检查并解码Flutter传递的dart-defines参数
if (project.hasProperty("dart-defines")) {
// 获取参数(用逗号分隔的Base64编码字符串)
val dartDefines: String = project.property("dart-defines") as String
dartDefines.split(",").forEach { encodedPair ->
try {
// 使用Kotlin/Java标准库的正确方式进行Base64解码
val decodedBytes = Base64.getDecoder().decode(encodedPair)
val decoded = String(decodedBytes, Charsets.UTF_8)
// 将"KEY=VALUE"格式的字符串拆分为键值对
val parts = decoded.split("=", limit = 2)
if (parts.size == 2) {
val key = parts[0]
val value = parts[1]
dartEnvironmentVariables[key] = value
// 打印每个解析出的变量(可选)
// println("[DEBUG] Parsed: $key = $value")
}
} catch (e: Exception) {
println("[WARN] Failed to decode dart-define pair: $encodedPair")
}
}
}
val appName = dartEnvironmentVariables["APP_NAME"] ?: "默认名称"
val appId = dartEnvironmentVariables["APP_ID"] ?: "默认包名"
println("🚀 =========================================")
println("🚀 构建配置信息 appName=${appName} appId=${appId}")
android {
android {
namespace = "com.sunclouder.mom"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
defaultConfig {
applicationId = appId //此包名,就是配置json 文件中的包名
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
/**
* arm64-v8a 64位 现代Android手机的绝对主流
* armeabi-v7a 32位 较旧的ARM设备 放弃
* x86_64 64位 Android模拟器、极少数的x86平板/电脑 放弃
* x86 32位 老旧的x86设备 放弃
*/
ndk {
abiFilters += listOf("arm64-v8a")
}
}
}
如上你可以在这个gradle.kts中读取之前dart配置的值
可选:为了让debug版本和release版本可以同时运行,并区分包名,测试版本名字后边带(测试),自定义多渠道打包 apk名称
html
buildTypes {
debug {
signingConfig = signingConfigs.getByName("debug")
applicationIdSuffix = ".debug" //debug下增加包名结尾
resValue("string", "app_name", "$appName(测试)")
}
release {
signingConfig = signingConfigs.getByName("release")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
resValue("string", "app_name", appName)
}
}
// 自定义多渠道打包 apk名称
applicationVariants.all {
val buildType = buildType.name
outputs.all {
if (this is com.android.build.gradle.internal.api.ApkVariantOutputImpl) {
val date = SimpleDateFormat("yyyyMMdd_HHmm", Locale.getDefault())
.format(Date())
outputFileName = when (buildType) {
"debug" -> {
"${appName}_v${versionName}_debug_${date}.apk"
}
"release" -> {
"${appName}_v${versionName}_release_${date}.apk"
}
else -> {
"${appName}_v${versionName}_test_${date}.apk"
}
}
}
}
}
如上只是可以打包为不同的包名和名称,但是对桌面icon_lacunher 还是无法修改,需要配置android Flaors
html
// 渠道配置(APP相关)
flavorDimensions += "app"
productFlavors {
//主包
create("appMain") {
dimension = "app"
println("🚀 构建配置信息 渠道 appMain")
}
//矿山
create("appKs") {
dimension = "app"
println("🚀 构建配置信息 渠道 appKs")
}
}
配置完如上后,你只需要在app各渠道包下增加res-mipmap-xxhdpi中增加图标就可以了目录如下
html
android
|--app
|--src
|新建渠道名称 appKS
|--mipmap-xxhdpi
ic_launcher.png
|appMain
|--mipmap-xxhdpi
ic_launcher.png
最后编译器配置
在运行旁边的按钮 Run / Debug Configurations 中edit config...
然后新增各个对应的配置,比如 appMainDebug 然后在 additional run args 中增加
--debug --verbose --flavor appMain --dart-define-from-file=config/app_main.json
appMainRelease :-release --verbose --flavor appMain --dart-define-from-file=config/app_main.json
同理其他渠道同样配置,然后可以选择对应的版本等进行run了。
然后打包 执行命令
flutter build apk --release --flavor appMain --dart-define-from-file=config/app_main.json --target-platform=android-arm64
当然可以写个sh脚本,直接运行选择
html
#!/bin/bash
# 简洁版Flutter打包脚本 (带等待指示)
echo "请选择要打包的配置:"
echo " 1) 主版本正式包 (app_main.json)"
echo " 2) 主版本测试包 (app_main.json)"
echo " 3) APP2正式包 (app_ks.json)"
echo " 4) APP2试包 (app_ks.json)"
echo ""
printf "请输入数字选择 (1-4): "
read choice
case $choice in
1) CONFIG="config/app_main.json"; BUILD_TYPE="release"; FLAVOR="appMain"; NAME="主版本正式包" ;;
2) CONFIG="config/app_main.json"; BUILD_TYPE="debug"; FLAVOR="appMain"; NAME="主版本测试包" ;;
3) CONFIG="config/app_ks.json"; BUILD_TYPE="release"; FLAVOR="appKs"; NAME=" APP2正式包" ;;
4) CONFIG="config/app_ks.json"; BUILD_TYPE="debug"; FLAVOR="appKs"; NAME=" APP2测试包" ;;
*) echo "无效选择"; exit 1 ;;
esac
echo ""
echo "📦 准备构建: $NAME"
echo "🔧 渠道: $FLAVOR, 类型: $BUILD_TYPE"
echo "🚀 开始执行..."
echo "> flutter build apk --$BUILD_TYPE --flavor $FLAVOR --dart-define-from-file=$CONFIG --target-platform=android-arm64"
# 直接执行并实时显示输出,同时捕获退出状态码
if flutter build apk --$BUILD_TYPE --dart-define-from-file=$CONFIG --target-platform=android-arm64; then
BUILD_STATUS=0
else
BUILD_STATUS=$?
fi
printf "\r"
# 显示结果
if [ $BUILD_STATUS -eq 0 ]; then
echo "✅ 构建成功!"
if [ "$BUILD_TYPE" = "release" ]; then
echo "📁 APK位置: $(pwd)/build/app/outputs/flutter-apk/app-release.apk"
else
echo "📁 APK位置: $(pwd)/build/app/outputs/flutter-apk/app-debug.apk"
fi
else
echo "❌ 构建失败!"
echo "📋 错误代码: $BUILD_STATUS"
# 这里可以添加其他错误处理逻辑
fi
echo ""
read -n 1 -s -r -p "操作完成,按任意键退出..."
echo ""