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:

  1. 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"