Custom script for generating environment-specific code for different platforms
Features
The flutter_env.dart file appears to be a custom script for generating environment-specific code for different platforms (Dart, Objective-C, and Gradle for Android). It reads from an environment file (.env by default), and generates code based on the key-value pairs in the environment file.
Here's a basic usage guide:
- Create an environment file: Create a .env file in your project root (or specify a different file using the envfile argument). This file should contain key-value pairs, one per line, like this:
API_KEY=123456
BASE_URL=https://api.example.com
2.Run the script: You can run the DotENV class with the command-line arguments. For example:
void main(List<String> args) {
DotENV(args);
}
You can pass arguments to specify the platform (platform), the environment file (envfile), and the directory name (dirname). If not specified, it will use default values.
3.Generated code: The script will generate a Dart file (lib/env.dart by default) with a class ENV that contains static string properties for each key-value pair in the environment file. For example:
class ENV {
static String API_KEY = "123456";
static String BASE_URL = "https://api.example.com";
}
You can then import this file in your Flutter code and access the environment variables like this: ENV.API_KEY.
Please note that this is a basic guide and the actual usage may vary depending on your project setup and requirements. Also, remember to exclude your environment files from version control to avoid committing sensitive data.
Unit Test Command
At the root of the project, run the following command:
flutter test test/dotenv_test.dart
Generate the env.dart file
At the root of the example
project, run the following command:
like: /Users/danli/Desktop/2024/packages/package_flutter_env/example
Because test is the development environment, the generated file is in the lib folder.
flutter test test/env_test.dart
trigger:
branches:
include:
- dev
parameters:
- name: env
displayName: "Build Environment"
type: string
default: dev
values:
- dev
- prod
variables:
- name: BUILD_MODE
value: "release"
- name: FIREBASE_APP_ID
value: "1:666195510318:android:9b570ce9e0c2c061820bbf"
stages:
####################################
# 🤖 Android: Build + Upload
####################################
- stage: Android_Stage
displayName: "Android Build & Upload"
pool:
vmImage: "ubuntu-latest"
jobs:
- job: Android
steps:
- task: FlutterInstall@0
inputs:
version: "latest"
- task: DownloadSecureFile@1
name: DownloadEnv
inputs:
secureFile: ".env.${{ parameters.env }}"
- script: |
echo "📦 Using .env.${{ parameters.env }}"
cp "$(Agent.TempDirectory)/.env.${{ parameters.env }}" .env
displayName: "Copy .env"
- script: |
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
displayName: "Generate env.g.dart"
- script: |
flutter build apk --release
displayName: "Build Android APK"
- script: |
echo "🚀 Installing Firebase CLI"
npm install -g firebase-tools
displayName: "Install Firebase CLI"
- script: |
APK_PATH="build/app/outputs/flutter-apk/app-release.apk"
if [ ! -f "$APK_PATH" ]; then
echo "❌ APK not found at $APK_PATH"
exit 1
fi
- script: |
APK_PATH="build/app/outputs/flutter-apk/app-release.apk"
if [ ! -f "$APK_PATH" ]; then
echo "❌ APK not found at $APK_PATH"
exit 1
fi
if [ "${{ parameters.env }}" = "prod" ]; then
# FIREBASE_GROUP="external-testers"
FIREBASE_GROUP="internal-testers"
else
FIREBASE_GROUP="internal-testers"
fi
echo "📤 Uploading $APK_PATH to Firebase"
firebase appdistribution:distribute "$APK_PATH" \
--app "$(FIREBASE_APP_ID)" \
--token "$(FIREBASE_CI_TOKEN)" \
--release-notes "Build for ${{ parameters.env }} environment" \
--groups "$FIREBASE_GROUP" \
--project "devopsautodeploy"
displayName: "Upload APK to Firebase"
####################################
# 🍏 iOS: Build + Upload
####################################
- stage: iOS_Stage
displayName: "iOS Build & Upload"
pool:
name: SelfHosted
jobs:
- job: iOS
variables:
APP_NAME: "Runner"
CONFIGURATION: "Release"
steps:
- task: DownloadSecureFile@1
name: DownloadEnv
inputs:
secureFile: ".env.${{ parameters.env }}"
- script: |
cp "$(Agent.TempDirectory)/.env.${{ parameters.env }}" .env
echo "✅ Copied .env.${{ parameters.env }}"
displayName: "Prepare .env"
- script: |
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
displayName: "Generate env.g.dart"
- script: |
cd ios
rm -rf Pods Podfile.lock
pod install --repo-update
displayName: "Install CocoaPods"
- script: |
flutter build ios --no-codesign --release
displayName: "Flutter build ios"
- script: |
cd ios
xcodebuild -workspace $APP_NAME.xcworkspace \
-scheme $APP_NAME \
-configuration $CONFIGURATION \
-sdk iphoneos \
-archivePath build/Build/${APP_NAME}.xcarchive \
archive
displayName: "Xcode Archive"
- script: |
cd ios
xcodebuild -exportArchive \
-archivePath build/Build/${APP_NAME}.xcarchive \
-exportPath build/export \
-exportOptionsPlist ExportOptions.plist
displayName: "Export IPA"
- task: AppStoreRelease@1
displayName: "Upload to TestFlight"
inputs:
ipaPath: "ios/build/export/*.ipa"
serviceEndpoint: "AppleServiceConnection"
releaseTrack: "TestFlight"
appIdentifier: "com.bitidi.app.test"
appType: "iOS"
trigger:
branches:
include:
- master
pool:
vmImage: "ubuntu-latest"
variables:
BUILD_MODE: "release"
FLUTTER_VERSION: "3.29.3"
stages:
- stage: Build_Android_AAB
displayName: "Build Android AAB"
jobs:
- job: BuildAAB
displayName: "Assemble Android AAB"
steps:
# Flutter & pub 缓存
# - task: Cache@2
# inputs:
# key: 'flutter | "$(Agent.OS)" | pubspec.yaml'
# restoreKeys: |
# flutter | "$(Agent.OS)"
# path: |
# ~/.pub-cache
# displayName: "Cache Flutter dependencies"
# 手动安装 Flutter SDK
- script: |
git clone https://github.com/flutter/flutter.git -b 3.29.3
echo "##vso[task.setvariable variable=FLUTTER_HOME]$(pwd)/flutter"
displayName: "Clone Flutter SDK"
# 将 Flutter bin 添加到 PATH
- script: |
export PATH="$FLUTTER_HOME/bin:$PATH"
flutter doctor
displayName: "Run Flutter Doctor"
# 下载 .env
- task: DownloadSecureFile@1
name: DownloadEnv
inputs:
secureFile: ".env"
- script: |
cp "$(Agent.TempDirectory)/.env" .env
displayName: "Copy .env to project root"
# 获取依赖 & 生成代码
- script: |
export PATH="$FLUTTER_HOME/bin:$PATH"
flutter pub get
flutter pub run build_runner build --delete-conflicting-outputs
displayName: "Flutter dependencies & code generation"
# 下载 keystore 文件
- task: DownloadSecureFile@1
name: DownloadKeystore
inputs:
secureFile: "devops-auto-deploy-key.jks"
- task: DownloadSecureFile@1
name: DownloadKeystoreProps
inputs:
secureFile: "key.properties"
- script: |
mkdir -p android/app
cp "$(Agent.TempDirectory)/devops-auto-deploy-key.jks" android/app/devops-auto-deploy-key.jks
cp "$(Agent.TempDirectory)/key.properties" android/key.properties
displayName: "Copy Keystore and Properties"
# 构建 AAB
- script: |
export PATH="$FLUTTER_HOME/bin:$PATH"
flutter build appbundle --release
displayName: "Build AAB"
# 收集 Artifact
- script: |
mkdir -p "$(Build.ArtifactStagingDirectory)/AndroidAAB"
cp build/app/outputs/bundle/release/app-release.aab "$(Build.ArtifactStagingDirectory)/AndroidAAB/"
displayName: "Collect AAB Artifact"
- task: PublishPipelineArtifact@1
inputs:
targetPath: "$(Build.ArtifactStagingDirectory)/AndroidAAB"
artifact: "AndroidAAB"
displayName: "Publish AAB Artifact"
- stage: Upload_GooglePlay
displayName: "Upload to Google Play Internal Testing"
dependsOn: Build_Android_AAB
jobs:
- job: UploadAAB
displayName: "Upload AAB to Google Play"
steps:
- download: current
artifact: AndroidAAB
displayName: "Download AAB artifact"
- task: GooglePlayRelease@4
inputs:
serviceEndpoint: "GooglePlayServiceConnection"
action: "SingleBundle"
applicationId: "com.bitidi.app.test"
bundleFile: "$(Pipeline.Workspace)/AndroidAAB/app-release.aab"
track: "internal"
isDraftRelease: true # 🔸 关键行:强制该发布为草稿状态
# rolloutPercentage: "100"