Flutter android 多渠道配置,多包名、icon、等配置。

为了方便可以使用三方库 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 ""
相关推荐
淡写成灰7 小时前
Flutter PopScope 返回拦截完整指南
flutter
ujainu7 小时前
Flutter与DevEco Studio协同开发:HarmonyOS应用实战指南
flutter·华为·harmonyos
zhangphil8 小时前
Android性能:trace上的锁竞争monitor contention with owner at
android
赵财猫._.8 小时前
【Flutter x 鸿蒙】第四篇:双向通信——Flutter调用鸿蒙原生能力
flutter·华为·harmonyos
砖厂小工8 小时前
Now In Android 精讲 9 - Benchmark 与 Baseline Profile
android
解局易否结局8 小时前
Flutter:跨平台开发的范式革新与实践之道
flutter
愤怒的代码8 小时前
深入解析 SystemUI 依赖注入:Dagger2 与 Hilt 核心机制重温
android·dagger
解局易否结局9 小时前
Flutter:跨平台开发的革命与实战指南
flutter