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 ""
相关推荐
千里马学框架3 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台3 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone3 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc3 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo3 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077003 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼3 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone3 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen3 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone3 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui