After developing a project with Flutter for some time, I found that it was too cumbersome to change the environment manually every time. As more and more parameters were added, it was easy to forget which parameter to change, resulting in environment switching problems. Therefore, I thought that Android could do multiple channels before, so I also wanted to try multiple channels with Flutter.

The preparatory work

Flutter command

In Flutter 1.17, Flutter will transmit dart-defines to iOS, Android, and macOS before it is compiled. Thus, darT-DEFINES defines can be invoked almost at any time.

--dart-define=DART_DEFINE_APP_ENV=debug
Copy the code

Flutter end processing

  • Declare environment variables
Abstract class EnvName {// Environment key static const String envKey = "DART_DEFINE_APP_ENV"; // environment value static const String debug = "debug"; static const String release = "release"; static const String debugServer = "debugServer"; }Copy the code
  • Declare the Environment Configuration
Class EnvConfig {/// Server URL final String? host; EnvConfig( {this.host }); }Copy the code
  • Environment definition
/ / development environment static final EnvConfig _debugConfig = EnvConfig (host: "http://129.211.191.80:10003/app",); / / the backend server local static final EnvConfig _debugServerConfig = EnvConfig (host: "http://192.168.1.162:10003/app",); / / release environment static final EnvConfig _releaseConfig = EnvConfig (host: "https://api.dzds.dianzedushu.com/app");Copy the code
  • Get the current environment
Static const appEnv = string.fromEnvironment (envname.envkey);Copy the code

You get the current environment from string.fromEnvironment, and then take different values based on the environment

  • Obtain configurations based on the environment
static EnvConfig _getEnvConfig() { switch (appEnv) { case EnvName.debug: return _debugConfig; case EnvName.debugServer: return _debugServerConfig; case EnvName.release: return _releaseConfig; default: return _releaseConfig; }}Copy the code

Use of environment variables

static String ServerUrl = Env.envConfig.host! ;Copy the code

compile

flutter run --dart-define=DART_DEFINE_APP_ENV=debug
Copy the code

So the compiled app is the environment that you set up

The effect


additional

Another problem is that you have to compile by command every time, so is there a way to compile directly from the IDE? Here is how to compile through the IDE

AS

Click on the place below

Then repeat to add release.

The following figure

Select the corresponding option at compile time.

vs code

Replace the contents of launch.json

{// Use IntelliSense to learn about related attributes. // Hover to view descriptions of existing properties. / / for more information, please visit: 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", ] } ] }Copy the code

Select the environment in debug mode.

Native multichannel

We can only modify the environment of flutter. Sometimes we need to use a third-party library to configure different data in the native environment. Don’t worry. How do I replace native data

Take my project as an example, the RONGyun SDK I access needs to configure keys of different environments.

ios

Ios generates variables through xcConfig.

We’ll find the DART inside the user – define – DEFINES inside will be more than a flutter. Inspector. StructuredErrors value, this apparently does not conform to the xcconfig key definitions. So the way to deal with this is to get rid of this value. The current environment is obtained based on the DART-DEFINES and the XCConfig is generated from the script.

Create a new scheme.

Add the script as shown.

Script code

# 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.xcconfigCopy the code

This generates the dartcbc.xcconfig file in the FLUTTER directory

Reference this file in debug.xcconfig and release.xcconfig respectively.

You can see it when you compile itRONGYUNIs added to a user-defined key

use

Create a new Dart. Xcconfig file in the Flutter directory

DART_DEFINE_APP_ENV=release
#include "DartDefines.xcconfig"

GCC_PREPROCESSOR_DEFINITIONS = $(inherited) RONGYUNKEY='$(RONGYUN)'
Copy the code

Reference this file in debug.xcconfig and release.xcconfig respectively.

NSString * rongyunKey = [RONGYUNKEY stringByAppendingPathExtension:@""]; / / production environment [[RCIMClient sharedRCIMClient] initWithAppKey: rongyunKey];Copy the code

android

Android is mainly generated using Gradle

Define values for multiple channels

Create a new dart.properties file in the project root directory and add the following

debug=rongyun=lmxuhwagl66vd,
debugServer=rongyun=lmxuhwagl66vd,
release=rongyun=z3v5yqkbz2
Copy the code

Generate environment values using Gradle

Add the following content to build. Gradle android under your app

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)Copy the code

Manifestplaceholder add RONGYUN: dartdefin.rongyun in defaultConfig.

Define meta in androidmanifest.xml

<meta-data
            android:name="RONGYUN"
            android:value="${RONGYUN}" />
Copy the code

Used in the Java


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

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

In this way, multi-channel construction is completely achieved!

The code word is not easy!!!!

Blog address