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 itRONGYUN
Is 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