拒绝魔改,flutter多渠道构建版本

用flutter开发项目一段时间后,发现切换环境实在太麻烦,每次都手动修改环境,随着参数的越来越多, 很容易就忘记修改哪个参数,导致环境切换出问题,所以就想到了android以前做是可以做多渠道的,所以也想试试flutter的多渠道。

准备工作

flutter命令

在 Flutter 1.17 中,Flutter 会将在编译前将 dart-defines 传递给 iOS、Android、macOS。
因此,可以几乎在各个时刻调用预先定义的 dart-defines。

1
--dart-define=DART_DEFINE_APP_ENV=debug

flutter端处理

  • 声明环境变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 声明的环境
    abstract class EnvName {
    // 环境key
    static const String envKey = "DART_DEFINE_APP_ENV";

    // 环境value
    static const String debug = "debug";
    static const String release = "release";

    static const String debugServer = "debugServer";
    }
  • 声明环境配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    ///环境配置
    class EnvConfig {
    ///服务器url
    final String? host;

    EnvConfig(
    {this.host
    });
    }
  • 环境定义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // 开发环境
    static final EnvConfig _debugConfig = EnvConfig(
    host: "http://129.211.191.80:10003/app",
    );
    // 后端本地服务器
    static final EnvConfig _debugServerConfig = EnvConfig(
    host: "http://192.168.1.162:10003/app",);

    // 发布环境
    static final EnvConfig _releaseConfig = EnvConfig(
    host: "https://api.dzds.dianzedushu.com/app");
  • 获取当前环境

    1
    2
    // 获取到当前环境
    static const appEnv = String.fromEnvironment(EnvName.envKey);

就是通过 String.fromEnvironment 来获取当前环境,然后根据环境去取不同的值

  • 根据环境获取相关配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    static EnvConfig _getEnvConfig() {
    switch (appEnv) {
    case EnvName.debug:
    return _debugConfig;
    case EnvName.debugServer:
    return _debugServerConfig;
    case EnvName.release:
    return _releaseConfig;

    default:
    return _releaseConfig;
    }
    }

环境变量使用

1
static String ServerUrl = Env.envConfig.host!;

编译

1
flutter run --dart-define=DART_DEFINE_APP_ENV=debug

这样编译的app 就是你设置的环境

效果


附加

这样又会有另一个问题,每次都要通过命令编译,那有没有办法通过ide 直接来编译呢. 下面就介绍如何通过ide 来编译

AS

点开下图的地方


在 additional run args 里面添加 --dart-define=DART_DEFINE_APP_ENV=debug

然后重复添加 release.

如下图

编译的时候选择对应的选项.

vs code

替换 launch.json 里面的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "flutter_app_2_0_debug",
"request": "launch",
"type": "dart",
"args": [
"--dart-define",
"DART_DEFINE_APP_ENV=debug",
]
},
{
"name": "flutter_app_2_0_server",
"request": "launch",
"type": "dart",
"args": [
"--dart-define",
"DART_DEFINE_APP_ENV=server",
]
},
{
"name": "flutter_app_2_0_release",
"request": "launch",
"type": "dart",
"args": [
"--dart-define",
"DART_DEFINE_APP_ENV=release",
]
}
]
}

在调试模式里面选择对应的环境.

原生多渠道

上面只能修改flutter 自己的环境, 有时候我们需要用到第三方库,需要再原生环境里面配置不同的数据,这时候怎么办呢.别急.来看看我怎么替换原生环境数据的

以我项目为例, 我接入的融云sdk 需要配置不同环境的key.

ios

ios 是通过 xcconfig 来生成变量的.

我们再 user-define 里面发现 DART-DEFINES 里面会多一个 flutter.inspector.structuredErrors 值, 这个貌似不符合xcconfig的key定义.所以处理方式是把这个值给处理掉. 然后根据dart-defines 获取当前环境 然后通过脚本生成xcconfig.

如图. 新建一个 scheme.

在如图位置 添加脚本.

脚本代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Type a script or drag a script file from your workspace to insert its path.
function urldecode() { : "${*//+/ }"; echo "${_//%/\\x}"; }

IFS=',' read -r -a define_items <<< "$DART_DEFINES"
debug_env=DART_DEFINE_APP_ENV=debug
debug_server_env=DART_DEFINE_APP_ENV=debugServer
for index in "${!define_items[@]}"
do
define_items[$index]=$(urldecode "${define_items[$index]}");
done

if [ ${#define_items[*]} > 1 ]; then
# # echo ${#define_items[*]}
# echo $define_items
if [ ${define_items[0]} = $debug_env ]; then
define_items[1]=RONGYUN=@"\"lmxuhwagl66vd\""
elif [ ${define_items[0]} = $debug_server_env ]; then
define_items[1]=RONGYUN=@"\"lmxuhwagl66vd\""
else
define_items[1]=RONGYUN=@"\"z3v5yqkbz22m0\""
fi
# unset define_items[1]
fi
# if [ ${#define_items[*]} > 0 ]; then
# define_items
# fi
# fi
# echo ${define_items[1]}
# printf $define_items
# unset define_items[1]
# printf $define_items
# printf "$s\n" "${define_items[@]}"
printf "%s\n" "${define_items[@]}" > ${SRCROOT}/Flutter/DartDefines.xcconfig

这样就在 flutter 目录生成了 DartDefines.xcconfig文件

Debug.xcconfigRelease.xcconfig 分别引用这个文件.

编译后 就可以看到 RONGYUN 被添加到 用户定义的key里面去了

使用

在 Flutter 目录新建一个 Dart.xcconfig文件

1
2
3
4
DART_DEFINE_APP_ENV=release
#include "DartDefines.xcconfig"

GCC_PREPROCESSOR_DEFINITIONS = $(inherited) RONGYUNKEY='$(RONGYUN)'

Debug.xcconfigRelease.xcconfig 分别引用这个文件.

1
2
3
NSString * rongyunKey = [RONGYUNKEY stringByAppendingPathExtension:@""];
//生产环境
[[RCIMClient sharedRCIMClient] initWithAppKey:rongyunKey];

android

安卓主要通过gradle 来生成

定义多渠道的值

在项目根目录新建一个 dart.properties 文件
添加如下内容

1
2
3
debug=rongyun=lmxuhwagl66vd,
debugServer=rongyun=lmxuhwagl66vd,
release=rongyun=z3v5yqkbz2

通过gradle 来生成环境值

在 app 下面的 build.gradle 的 android 里面添加如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/// 设置默认配置参数
def dartDefine = [
DART_DEFINE_APP_ENV: 'debug',
]
if (project.hasProperty('dart-defines')) {
dartDefine = dartDefine + project.property('dart-defines')
.split(',')
.collectEntries { entry ->
def pair = URLDecoder.decode(entry).split('=')
[(pair.first()): pair.last()]
}
}

println(dartDefine)

def dartFile = rootProject.file("dart.properties")

def dartProperties = new Properties()
dartProperties.load(new FileInputStream(dartFile))

println(dartProperties)

def currentEvn = dartDefine.DART_DEFINE_APP_ENV

println(currentEvn)

def currentProperties = dartProperties["$currentEvn"]
//
println(currentProperties)

dartDefine = dartDefine + currentProperties
.split(',')
.collectEntries { entry ->
def pair = URLDecoder.decode(entry).split('=')
[(pair.first()): pair.last()]
}
println(dartDefine)

在 defaultConfig 中的 manifestPlaceholders 添加 RONGYUN : dartDefine.rongyun.

androidmanifest.xml 中定义 meta

1
2
3
<meta-data
android:name="RONGYUN"
android:value="${RONGYUN}" />

java中使用

1
2
3
4
5

val appInfo: ApplicationInfo = this.packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)

val rongyunKey: String? = appInfo.metaData.getString("RONGYUN")
RongIMClient.init(this, "$rongyunKey")

这样就完全做到了多渠道构建!

码字不易!!!!

Adhere to original technology sharing, your support will encourage me to continue to create!