Experiment Flutter SDK
Official documentation for Amplitude Experiment's Client-side Flutter SDK.
Install
Add the amplitude_experiment package to your pubspec.yaml:
dependencies:
amplitude_experiment: ^0.1.0-beta.1
Then run:
flutter pub get
Web installation (optional)
The Flutter Experiment SDK uses Dart's JavaScript interoperability to enable the Experiment JavaScript SDK for Flutter Web. This requires that you make the SDK available within the global JavaScript scope. Add the following script tag to web/index.html in your Flutter project:
<script src="https://unpkg.com/@amplitude/experiment-js-client@1.20.1/dist/experiment.umd.js"></script>
Platform support
This SDK supports Android, iOS, and Web platforms.
Quick start
The right way to initialize the Experiment SDK depends on whether you use an Amplitude SDK for analytics or a third party (for example, Segment).
import 'package:amplitude_flutter/amplitude.dart';
import 'package:amplitude_flutter/configuration.dart';
import 'package:amplitude_experiment/amplitude_experiment.dart';
// (1) Initialize the Amplitude Analytics SDK.
final amplitude = Amplitude(Configuration(apiKey: 'API_KEY'));
await amplitude.isBuilt;
// (2) Initialize the experiment client with Amplitude Analytics.
final experiment = await Experiment.initializeWithAmplitude(
'DEPLOYMENT_KEY',
ExperimentConfig(),
);
// (3) Fetch variants and await the result.
await experiment.fetch();
// (4) Look up a flag's variant.
final variant = await experiment.variant('FLAG_KEY');
if (variant.value == 'on') {
// Flag is on
} else {
// Flag is off
}
Initialize
The SDK client should be initialized in your application on startup. The deployment key argument passed into the apiKey parameter must live within the same project that you are sending analytics events to.
static Future<ExperimentClient> initializeWithAmplitude(String apiKey, ExperimentConfig config)
| Parameter | Requirement | Description |
|---|---|---|
apiKey | required | The deployment key which authorizes fetch requests and determines which flags should be evaluated for the user. |
config | required | The client configuration used to customize SDK client behavior. |
The initializer returns a singleton instance, so subsequent initializations for the same instance name always return the initial instance. To create multiple instances, use the instanceName configuration.
import 'package:amplitude_flutter/amplitude.dart';
import 'package:amplitude_flutter/configuration.dart';
import 'package:amplitude_experiment/amplitude_experiment.dart';
final amplitude = Amplitude(Configuration(apiKey: 'API_KEY'));
await amplitude.isBuilt;
final experiment = await Experiment.initializeWithAmplitude(
'DEPLOYMENT_KEY',
ExperimentConfig(),
);
Instance name
If you're using a custom instance name for analytics, you need to set the same value in the instanceName configuration option in the experiment SDK, or vice versa. Both SDKs default to $default_instance.
Configuration
SDK client configuration occurs during initialization.
logLevel- Description: The minimum log level to output. Options:
LogLevel.none,LogLevel.error,LogLevel.warn,LogLevel.info,LogLevel.debug. - Default Value:
LogLevel.warn
- Description: The minimum log level to output. Options:
fallbackVariant- Description: The default variant to fall back to if a variant for the provided key doesn't exist.
- Default Value:
null
initialVariants- Description: An initial set of variants to access. This field is valuable for bootstrapping the client SDK with values rendered by the server using server-side rendering (SSR).
- Default Value:
{}
source- Description: The primary source of variants. Set the value to
Source.initialVariantsand configureinitialVariantsto bootstrap the SDK for SSR or testing purposes. - Default Value:
Source.localStorage
- Description: The primary source of variants. Set the value to
serverZone- Description: Select the Amplitude data center to get flags and variants from,
usoreu. - Default Value:
ServerZone.us
- Description: Select the Amplitude data center to get flags and variants from,
serverUrl- Description: The host to fetch remote evaluation variants from. For hitting the EU data center, use
serverZone. - Default Value:
https://api.lab.amplitude.com
- Description: The host to fetch remote evaluation variants from. For hitting the EU data center, use
flagsServerUrl- Description: The host to fetch local evaluation flags from. For hitting the EU data center, use
serverZone. - Default Value:
https://flag.lab.amplitude.com
- Description: The host to fetch local evaluation flags from. For hitting the EU data center, use
fetchTimeoutMillis- Description: The timeout for fetching variants in milliseconds.
- Default Value:
10000
retryFetchOnFailure- Description: Whether to retry variant fetches in the background if the request doesn't succeed.
- Default Value:
true
automaticExposureTracking- Description: If true, calling
variant()tracks an exposure event through the configuredtrackingProvider. If no tracking provider is set, this configuration option does nothing. - Default Value:
true
- Description: If true, calling
fetchOnStartpollOnStart- Description: Poll for local evaluation flag configuration updates once per minute on start.
- Default Value:
false
automaticFetchOnAmplitudeIdentityChange- Description: Only matters if you use
initializeWithAmplitude. Iftrue, any change to the user ID, device ID, or user properties from analytics triggers the experiment SDK to fetch variants and update its cache. - Default Value:
false
- Description: Only matters if you use
userProvider- Description: An interface used to provide the user object to
fetch()when called. See User provider. - Default Value:
null
- Description: An interface used to provide the user object to
trackingProvider- Description: Implement and configure this interface to track exposure events through the experiment SDK, either automatically or explicitly. See Exposure tracking provider.
- Default Value:
null
instanceName- Description: Custom instance name for experiment SDK instance. The value of this field is case-sensitive.
- Default Value:
$default_instance
initialFlags- Description: A JSON string representing an initial set of flag configurations to use for local evaluation.
- Default Value:
null
EU data center
If you're using Amplitude's EU data center, configure the serverZone option on initialization to ServerZone.eu.
Integrations
If you use Amplitude Analytics SDK to track events into Amplitude, Amplitude recommends that you set up an integration on initialization. Integrations implement provider interfaces to enable a more streamlined developer experience by making it easier to manage user identity and track exposure events.
Fetch
Fetches variants for a user and stores the results in the client for fast access. This function remote evaluates the user for flags associated with the deployment used to initialize the SDK client.
Future<void> fetch([ExperimentUser? user, FetchOptions? options])
| Parameter | Requirement | Description |
|---|---|---|
user | optional | Explicit user information to pass with the request to evaluate. This user information is merged with user information provided from integrations via the user provider, preferring properties passed explicitly to fetch() over provided properties. |
options | optional | Fetch options such as a list of specific flag keys to fetch. |
Amplitude Experiment recommends calling fetch() during application start up so that the user gets the most up-to-date variants for the application session. Furthermore, you should wait for the fetch request to return a result before rendering the user experience to avoid the interface "flickering".
final user = ExperimentUser(
userId: 'user@company.com',
deviceId: 'abcdefg',
userProperties: {'premium': true},
);
await experiment.fetch(user);
If you're using an integration or a custom user provider then you can fetch without inputting the user.
await experiment.fetch();
Fetch when user identity changes
If you want the most up-to-date variants for the user, it's recommended that you call fetch() whenever the user state changes in a meaningful way. For example, if the user logs in and receives a user ID, or has a user property set which may affect flag or experiment targeting rules.
In the case of user properties, Amplitude recommends passing new user properties explicitly to fetch() instead of relying on user enrichment prior to remote evaluation. This is because user properties that are synced remotely through a separate system have no timing guarantees with respect to fetch()--i.e. a race.
If fetch() times out (default 10 seconds) or fails for any reason, the SDK client will return and retry in the background with back-off. You may configure the timeout or disable retries in the configuration options when the SDK client is initialized.
Start
Fetch vs start
Use start if you're using client-side local evaluation. If you're only using remote evaluation, call fetch instead of start.
Start the Experiment SDK to get flag configurations from the server and fetch remote evaluation variants for the user. The SDK is ready once the returned future completes.
Future<void> start(ExperimentUser? user)
| Parameter | Requirement | Description |
|---|---|---|
user | optional | Explicit user information to pass with the request to fetch variants. This user information merges with user information from any integrations through the user provider, and prefers properties passed explicitly to fetch() over provided properties. Also sets the user in the SDK for reuse. |
Call start() when your application is initializing, after user information is available to use to evaluate or fetch variants. The returned future completes after loading local evaluation flag configurations and fetching remote evaluation variants.
Set fetchOnStart in the SDK configuration to control the behavior of start() to improve the performance of your application.
- If your application never relies on remote evaluation, set
fetchOnStarttofalseto avoid increased startup latency caused by remote evaluation. - If your application relies on remote evaluation, but not right at startup, you may set
fetchOnStarttofalseand callfetch()and await the future separately.
await experiment.start(null);
Variant
Access a variant for a flag or experiment from the SDK client's local store.
Automatic exposure tracking
When an integration is used or a custom exposure tracking provider is set, variant() automatically tracks an exposure event through the tracking provider. To disable this functionality, configure automaticExposureTracking to be false, and track exposures manually using exposure().
Future<Variant> variant(String flagKey, [Variant? fallbackVariant])
| Parameter | Requirement | Description |
|---|---|---|
flagKey | required | The flag key to identify the flag or experiment to access the variant for. |
fallbackVariant | optional | The value to return if no variant was found for the given flagKey. |
When determining which variant a user has been bucketed into, compare the variant value to a well-known string.
final variant = await experiment.variant('FLAG_KEY');
if (variant.value == 'on') {
// Flag is on
} else {
// Flag is off
}
Access a variant's payload
A variant may also be configured with a dynamic payload of arbitrary data. Access the payload field from the variant object after checking the variant's value.
final variant = await experiment.variant('FLAG_KEY');
if (variant.value == 'on') {
final payload = variant.payload;
}
A null variant value means that the user hasn't been bucketed into a variant. You may use the built in fallback parameter to provide a variant to return if the store doesn't contain a variant for the given flag key.
final variant = await experiment.variant(
'FLAG_KEY',
Variant(value: 'control'),
);
if (variant.value == 'control') {
// Control
} else if (variant.value == 'treatment') {
// Treatment
}
All
Access all variants stored by the SDK client.
Future<Map<String, Variant>> all()
Clear
Clear all variants in the cache and storage.
Future<void> clear()
You can call clear after user logout to clear the variants in cache and storage.
await experiment.clear();
Exposure
Manually track an exposure event for the current variant of the given flag key through configured integration or custom exposure tracking provider. Generally used in conjunction with setting the automaticExposureTracking configuration option to false.
Future<void> exposure(String flagKey)
| Parameter | Requirement | Description |
|---|---|---|
flagKey | required | The flag key to identify the flag or experiment variant to track an exposure event for. |
final variant = await experiment.variant('FLAG_KEY');
// Do other things...
await experiment.exposure('FLAG_KEY');
if (variant.value == 'control') {
// Control
} else if (variant.value == 'treatment') {
// Treatment
}
Providers
Integrations
If you use the Amplitude Analytics SDK alongside the Experiment Client SDK, Amplitude recommends you use an integration instead of implementing custom providers.
Provider implementations enable a more streamlined developer experience by making it easier to manage user identity and track exposure events.
User provider
The user provider is used by the SDK client to access the most up-to-date user information only when it's needed (for example, when fetch() is called). This provider is optional, but helps if you have a user information store already set up in your application. This way, you don't need to manage two separate user info stores in parallel, which may result in a divergent user state if the application user store is updated and experiment isn't (or vice versa).
abstract interface class UserProvider {
ExperimentUser getUser();
}
To use your custom user provider, set the userProvider configuration option with an instance of your custom implementation on SDK initialization.
class CustomUserProvider implements UserProvider {
@override
ExperimentUser getUser() {
// Return the current user from your app's user store
return ExperimentUser(userId: 'user@company.com');
}
}
final experiment = await Experiment.initialize(
'DEPLOYMENT_KEY',
ExperimentConfig(
userProvider: CustomUserProvider(),
),
);
Exposure tracking provider
Implementing an exposure tracking provider is highly recommended. Exposure tracking increases the accuracy and reliability of experiment results and improves visibility into which flags and experiments a user is exposed to.
abstract interface class ExposureTrackingProvider {
void track(Exposure exposure);
}
The implementation of track() should track an event of type $exposure (a.k.a name) with two event properties, flag_key and variant, corresponding to the two fields on the Exposure object argument. Finally, the event tracked must eventually end up in Amplitude Analytics for the same project that the deployment used to initialize the SDK client lives within, and for the same user that variants were fetched for.
To use your custom exposure tracking provider, set the trackingProvider configuration option with an instance of your custom implementation on SDK initialization.
class CustomExposureTracker implements ExposureTrackingProvider {
@override
void track(Exposure exposure) {
// Track the exposure event to your analytics provider
analytics.track('\$exposure', {
'flag_key': exposure.flagKey,
'variant': exposure.variant,
});
}
}
final experiment = await Experiment.initialize(
'DEPLOYMENT_KEY',
ExperimentConfig(
trackingProvider: CustomExposureTracker(),
),
);
Bootstrapping
You may want to bootstrap the experiment client with an initial set of flags and variants when variants are obtained from an external source (for example, not from calling fetch() on the SDK client). Use cases include local evaluation, server-side rendering, or integration testing on specific variants.
Initial variants
To bootstrap the client, set the flags and variants in the initialVariants configuration option, then set the source to Source.initialVariants so that the SDK client prefers the bootstrapped variants over any previously fetched and stored variants for the same flags.
final experiment = await Experiment.initialize(
'DEPLOYMENT_KEY',
ExperimentConfig(
initialVariants: {
'flag-key-1': Variant(value: 'on'),
'flag-key-2': Variant(value: 'treatment'),
},
source: Source.initialVariants,
),
);
Initial flags
To bootstrap the client with a set of flag configurations for local evaluation, set the initialFlags configuration option with a JSON string of flag configurations.
final experiment = await Experiment.initialize(
'DEPLOYMENT_KEY',
ExperimentConfig(
initialFlags: '{"flag-key-1": ...}',
),
);
Was this helpful?