24

Is it possible to get flavor name Flutter side? for both android and ios

build.gradle

flavorDimensions "app"

productFlavors {
    dev {
        dimension "app"
        versionCode 2
        versionName "1.0.0"
    }
    qa {
        dimension "app"
        applicationId "com.demo.qa"
        versionCode 3
        versionName "1.0.2"
    }
}
BIS Tech
  • 17,000
  • 12
  • 99
  • 148
  • 2
    What is a main purpose of getting flavor name? Use different configuration based on flavor name in the dart code? Or you just want to get flavor name? – tatsuDn May 12 '20 at 08:18

5 Answers5

30

As long as every flavor has a different packageName you could do it like this:

enum EnvironmentType { dev, qa }

class Environment {
  EnvironmentType current;

  Environment() {
    PackageInfo.fromPlatform().then((PackageInfo packageInfo) {
      switch (packageInfo.packageName) {
        case "com.demo.qa":
          current = EnvironmentType.qa;
          break;
        default:
          current = EnvironmentType.dev;
      }
    });
  }
}
Josh
  • 991
  • 6
  • 14
  • 1
    Nice solution, but packag_info was replaced by a community version that depends on fullter framework and can't run as the first line in main – Asaf Pinhassi Jan 14 '22 at 18:25
  • This shouldn't be a problem as you could just instantiate the class after Flutter is loaded. One possibility would be to add a TransitionBuilder to the MaterialApp and setup your dependencies there. – Josh Jan 17 '22 at 10:06
  • I needed it to init Sentry logging - it needs to be way before the UI – Asaf Pinhassi Jan 19 '22 at 21:44
  • 2
    Note this doesn't work on web. Use Till Friebe's solution instead. – E. Sun Mar 18 '22 at 15:57
30

You could use the solution from jonahwilliams.

  1. Set a key-value pair when creating the app:
flutter build apk --flavor=paid --dart-define=app.flavor=paid
  1. Access the value in Dart:
const String flavor = String.fromEnvironment('app.flavor');

void main() {
  print(flavor);
}

This would print "paid" when run.

The advantages are that you can use this on platforms where flavors are not supported yet and that the variable is const. The disadvantage that you have to set the key-value pair for the build.

Till Friebe
  • 1,280
  • 15
  • 27
  • This is exactly what I used. Works great when building for android because you can pass it with the shell command. Still need to know how to pass dart-define when building an archive from xcode. – shadysamir Dec 30 '20 at 20:30
  • @shadysamir it's similar to above when building from the command line. `flutter build ipa --flavor=paid --dart-define=app.flavor=paid` This link will help a bit. https://docs.flutter.dev/deployment/ios – Nick N Jan 17 '22 at 19:05
  • @nick yeah shell build archive was not released yet in stable back then. But it's been for a while now. – shadysamir Jan 18 '22 at 20:04
  • 2
    I guess, there are many benefit that we can handle flavor type as `const` like set default value of freezed field or function default arguments. – MJ Studio Dec 17 '22 at 16:35
4

There is not a simple way to know the flavor name, however I would suggest you to use an environment variable, loaded from flutter_dotenv for example.

file .env

FLAVOR=dev

file main.dart

void main() async {
  await DotEnv().load('.env');
  final flavor = DotEnv().env['FLAVOR'];

  String baseUrl;
  if (flavor == 'dev') {
    baseUrl = 'https://dev.domain.com';
  } else if (flavor == 'qa') {
    baseUrl = 'https://qa.domain.com';
  } else {
    throw UnimplementedError('Invalid FLAVOR detected');
  }
}

This will allow you (as developer) to easily change the behaviour of your app and switch from different environments seamlessy.

NeoKree
  • 411
  • 4
  • 13
4

I haven't seen any answer describing how to get the current flavor directly from runtime config, after a lot of search I found this way: For Android is quite simple, put this code in your MainActivity.kt:


    private val CHANNEL = "flavor_channel"

  override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
      call, result ->
            when (call.method) {
                "getFlavor" -> {
                    result.success(BuildConfig.FLAVOR)
                }
            }
    }
  }

For iOS, put that in the AppDelegate.swift:

let controller : FlutterViewController = window?.rootViewController as! FlutterViewController
let flavorChannel = FlutterMethodChannel(name: "flavor_channel",
                                          binaryMessenger: controller.binaryMessenger)
flavorChannel.setMethodCallHandler({
  (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in
  // This method is invoked on the UI thread.
  guard call.method == "getFlavor" else {
    result(FlutterMethodNotImplemented)
    return
  }
  self.receiveCurrentFlavor(result: result)
})

Then create this method at the bottom of AppDelegate class:

    private func receiveCurrentFlavor(result: FlutterResult) {
    var config: [String: Any]?
        
    if let infoPlistPath = Bundle.main.url(forResource: "Info", withExtension: "plist") {
        do {
            let infoPlistData = try Data(contentsOf: infoPlistPath)
            
            if let dict = try PropertyListSerialization.propertyList(from: infoPlistData, options: [], format: nil) as? [String: Any] {
                config = dict
            }
        } catch {
            print(error)
        }
    }

    result(config?["Flavor"])
  }

We now need to make the Flavor available in the infoDictionary. In Xcode, open Runner/Info.plist and add a key Flavor mapped to ${PRODUCT_FLAVOR}: info.plist

Now, under Targets/Runner/Build Settings/User-Defined and add a setting PRODUCT_FLAVOR. Add the flavor name for each configuration: User-Defined settings

Then in Dart:

final flavor =  await const MethodChannel("flavor_channel").invokeMethod("getFlavor");

Then you're done!

2

Just write a channel method for that.

 override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
        super.configureFlutterEngine(flutterEngine)

        channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
        channel.setMethodCallHandler { call, result ->
            when (call.method) {
                "getFlavor" -> {
                    result.success(BuildConfig.FLAVOR)
                }
            }
        }
    }

Than in dart:

final flavor = await platform.invokeMethod("getFlavor");