likes
comments
collection
share

Flutter 多环境、多渠道自动打包

作者站长头像
站长
· 阅读数 20

本文主要解决在多环境(测试环境,开发环境等),多渠道情况下,如何优雅快速的进行Flutter打包。

Flutter中的配置

定义一个类,用于从环境中获取自定义的渠道名,这里指定的key是ANDROID_CHANNEL,可以根据自己需要定义key。

class EnvConfig{
  /// flutter run --dart-define=ANDROID_CHANNEL=flutter
  /// 多渠道打包
  /// Android Studio可以配置运行Flutter参数 --dart-define=ANDROID_CHANNEL=flutter
  static const CHANNEL=String.fromEnvironment('ANDROID_CHANNEL',defaultValue: 'unknown');
}

也可以从环境中获取多个,不同类型的内容。

class EnvConfig{
  static const CHANNEL=String.fromEnvironment('ANDROID_CHANNEL',defaultValue: 'unknown');
  static const VERSION=int.fromEnvironment('ANDROID_VERSION',defaultValue: 1);
}

此时在命令中运行参数:

flutter build apk --dart-define=ANDROID_CHANNEL=xiaomi

这样CHANNEL的值就是xiaomi了,也就是所谓的渠道名。如果是多个--dart-define,中间空格隔开即可。

flutter build apk --dart-define=ANDROID=xiaomi --dart-define=ANDROID_VERSION=2

这样就可以根据变量CHANNEL进行业务逻辑操作,例如友盟统计渠道。

Android Studio 配置

日常开发,不可能经常手刃命令行,配置run参数。

Flutter 多环境、多渠道自动打包

配置run args,内容和上面一致。

Flutter 多环境、多渠道自动打包

如果需要配置多个环境,例如开发环境,测试环境,正式环境,也可以通过这种方式来进行,避免手动修改代码,出错。

  1. 复制已有的配置内容
  2. 修改配置名称name,例如生产环境:main-prod
  3. 修改不同环境参数内容

Flutter 多环境、多渠道自动打包

apk包名输出

在编译或者打包时,Flutter会将--dart-define参数传递给Android的APP组件的build.gradle文件。因此可以通过下面方式获取内容。

def dartEnvironmentVariables = [
        ANDROID_CHANNEL: 'android',
]

if (project.hasProperty('dart-defines')) {
    println('print dart defines proerty')
    dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
            .split(',')
            .collectEntries { entry ->
                def pair = new String(entry.decodeBase64(), 'UTF-8').split('=')
                println(pair.first()+':'+pair.last())
                [(pair.first()): pair.last()]
            }
}

这里有个注意事项

Flutter传递过来的dart-definesvalue是会进行加密的,所以需要进行解密,而且不同版本的Flutter加密方式也不一样。在Flutter 2.2采用上面代码entry.decodeBase64()。而老版本采用下面代码URLDecoder.decode(entry)

def dartEnvironmentVariables = [
    ANDROID_CHANNEL: 'android',
    OTHER_VAR: 'other',
]

if (project.hasProperty('dart-defines')) {
    dartEnvironmentVariables = dartEnvironmentVariables + project.property('dart-defines')
        .split(',')
        .collectEntries { entry ->
            def pair = URLDecoder.decode(entry).split('=')
            [(pair.first()): pair.last()]
        }
} 

如果发现到获取不了,可以通过打印project.property('dart-defines')内容,看看是什么编码格式,或者查阅最新的文档。

获取到渠道名后,就是配置apk名称了,这里只在正式环境才输出自定义包名。

android{
  applicationVariants.all { variant ->
      variant.outputs.all { output ->
          if(variant.buildType.name == "release"){
              def newApkName="${dartEnvironmentVariables.ANDROID_CHANNEL}_app_v${defaultConfig.versionName}_${defaultConfig.versionCode}.apk"
              outputFileName = new File(newApkName)
          }
      }
  }
}

ok,这样打包输出的包名就自带渠道了。路径应该是项目目录/build/app/outputs/apk/release

自动化打包

通过上面方式,每次打包只能打一个渠道包,那么多应用商店,累啊。

在项目根目录,写脚本build_channel.sh,内容如下。

flutter build apk --dart-define=ANDROID_CHANNEL=$1 
cd 项目根目录/build/app/outputs/apk/release
mv  *.apk 其他目录

其中项目根目录需要更改成自己实际项目目录。由于每次打包会覆盖之前的apk,所以打包完一个apk,需要移动到其他文件夹。

再创建脚本build_apk.sh

build_channel.sh xiaomi
build_channel.sh huawei
build_channel.sh meizu
build_channel.sh vivo
build_channel.sh oppo
build_channel.sh yingyongbao
build_channel.sh m360

根据自己的渠道,按上面格式添加即可。

最后通过执行build_apk.sh脚本就可以了。另外注意两个脚本的权限问题chomd 777 xxx