Flutter plugin
Add feature flags and experimentation to your cross-platform mobile apps with the Harness FME Flutter plugin.
This guide provides detailed information about our Flutter plugin which is built on top of our Android, iOS and Browser SDKs. The plugin provides you a way to interact with the native SDKs.
All of our SDKs are open source. Go to our Flutter GitHub repository to see the source code.
Before you begin
This plugin supports Android, iOS, and Web platforms.
For Android and iOS, Dart SDK v2.16.2 or later and Flutter v2.5.0 or later are required.
For Web, Dart SDK v3.3.0 or later and Flutter v3.19.0 or later are required. The package is compatible with WebAssembly (WASM) compilation.
Initialization
Set up Harness FME in your code base with the following steps:
1. Add the package in your pubspec.yaml file
dependencies:
splitio: 1.2.12. Instantiate the plugin
/// Initialize Split plugin
import 'package:splitio/split_client.dart';
import 'package:splitio/splitio.dart';
/// KEY represents your internal user id, or the account id that
/// the user belongs to.
/// This could also be a cookie you generate for anonymous users.
final Splitio _split = Splitio('YOUR_SDK_KEY', 'KEY');We recommend instantiating the Splitio object once as a singleton and reusing it throughout your application.
Configure the plugin with the SDK key for the FME environment that you would like to access. In legacy Split (app.split.io) the SDK key is found on your Admin settings page, in the API keys section. Select a client-side SDK API key. This is a special type of API token with limited privileges for use in browsers or mobile clients. See API keys to learn more.
Using the plugin
Basic use
When the SDK is instantiated, it starts background tasks to update an in-memory cache with small amounts of data fetched from Harness servers. This process can take up to a few hundred milliseconds, depending on the size of data. If the SDK is asked to evaluate which treatment to show to a customer for a specific feature flag while it's in this intermediate state, it may not have the data necessary to run the evaluation. In this case, the SDK does not fail, rather, it returns the control treatment.
To make sure the SDK is properly loaded before asking it for a treatment, wait until the SDK is ready, as shown below. You can use the onReady parameter when creating the client to get notified when this happens.
After the observable calls back, you can use the getTreatment method to return the proper treatment based on the FEATURE_FLAG_NAME and the key variable you passed when instantiating the SDK. Then use an if-else-if block as shown below and insert the code for the different treatments that you defined in Harness FME. Remember the final else branch in your code to handle the client returning control.
Attribute syntax
To target based on custom attributes, pass the client's getTreatment method as an attribute map at runtime.
In the example below, we are rolling out a feature flag to users. The provided attributes plan_type, registered_date, permissions, and deal_size are passed to the getTreatment call. These attributes are compared and evaluated against the attributes used in the rollout plan as defined in Harness FME to decide whether to show the on or off treatment to this account. The getTreatment method supports five types of attributes: strings, numbers, dates, booleans, and sets. The proper data type and syntax for each are:
Strings: Use type String.
Numbers: Use type num (int or double).
Dates: Express the value in milliseconds since epoch. Note: Milliseconds since epoch is expressed in UTC. If your date or date-time combination is in a different timezone, first convert it to UTC, then transform it to milliseconds since epoch.
Booleans: Use type bool.
Sets: Use type List or Set.
Binding attributes to the client
Attributes can optionally be bound to the client at any time during the SDK lifecycle. These attributes are stored in memory and used in every evaluation to avoid the need to keep the attribute set accessible through the whole app. When an evaluation is called, the attributes provided (if any) at evaluation time are combined with the ones that are already loaded into the SDK memory, with the ones provided at function execution time taking precedence. This enables those attributes to be overridden or hidden for specific evaluations.
An attribute is considered valid if it follows one of the types listed below:
String
Number
Boolean
Array
The SDK validates these before storing them and if there are invalid or missing values, possibly indicating an issue, the methods return the boolean false and do not update any value.
To use these methods, refer to the example below:
Multiple evaluations at once
In some instances, you may want to evaluate treatments for multiple feature flags at once. Use the different variations of getTreatments from the SDK factory client to do this.
getTreatments: Pass a list of the feature flag names you want treatments for.getTreatmentsByFlagSet: Evaluate all flags that are part of the provided set name and are cached on the SDK instance.getTreatmentsByFlagSets: Evaluate all flags that are part of the provided set names and are cached on the SDK instance.
Get treatments with configurations
To leverage dynamic configurations with your treatments, use the getTreatmentWithConfig method.
This method returns an object containing the treatment and associated configuration:
The config element is a stringified version of the configuration JSON defined in Harness FME. If there is no configuration defined for a treatment, the SDK returns null for the config parameter.
This method taskes the exact set of arguments as the standard getTreatment method. See below examples on proper usage:
If you need to get multiple evaluations at once, you can also use the getTreatmentsWithConfig methods. These methods take the exact same arguments as the getTreatments methods but return a mapping of feature flag names to SplitResult objects instead of strings. Example usage below.
If a flag cannot be evaluated, the SDK returns the fallback treatment value (default "control" unless overridden globally or per flag). For more information, see Fallback treatments.
Append properties to impressions
Impressions are generated by the SDK each time a getTreatment method is called. These impressions are periodically sent back to Harness servers for feature monitoring and experimentation.
You can append properties to an impression by passing an object of key-value pairs to the getTreatment method. These properties are then included in the impression sent by the SDK and can provide useful context to the impression data.
Three types of properties are supported: strings, numbers, and booleans.
Shutdown
Call the client.destroy() method once you've stopped using the client, as this method gracefully shuts down the SDK by stopping all background threads, clearing caches, closing connections, and flushing the remaining unpublished impressions.
After destroy() is called and finishes, any subsequent invocations to getTreatment/getTreatments or manager methods result in control or empty list, respectively.
Track
Use the track method to record any actions your customers perform. Each action is known as an event and corresponds to an event type. Calling track through one of our SDKs or via the API is the first step to getting experimentation data into Harness FME and allows you to measure the impact of your feature flags on your users' actions and metrics. Learn more about using track events.
In the examples below, you can see that the .track() method can take up to four arguments. The proper data type and syntax for each are:
EVENT_TYPE: The event type that this event should correspond to. The expected data type is String. Full requirements on this argument are:
Contains 63 characters or fewer.
Starts with a letter or number.
Contains only letters, numbers, hyphen, underscore, or period.
This is the regular expression we use to validate the value:
[a-zA-Z0-9][-_\.a-zA-Z0-9]{0,62}
TRAFFIC_TYPE: (Optional) The traffic type of the key in the track call. The expected data type is String. You can only pass values that match the names of traffic types that you have defined Harness FME.
VALUE: (Optional) The value is used to create the metric. The expected data type is double.
PROPERTIES: (Optional) An object of key value pairs that can be used to filter your metrics. Learn more about event properties. FME currently supports three types of properties: strings, numbers, and booleans.
The track method returns a boolean value of true or false to indicate whether or not the client was able to successfully queue the event to be sent back to Harness servers on the next event post. The service returns false if the current queue size is equal to the config set by eventsQueueSize or if an incorrect input to the track method is provided.
In the case that a bad input is provided, you can read more about our SDK's expected behavior.
Configuration
The SDK has a number of knobs for configuring performance. Each knob is tuned to a reasonable default. However, you can override values when instantiating the Splitio:
The parameters available for configuration are shown below.
Configuration
Description
Default value
Supported platforms
featuresRefreshRate
The SDK polls Harness servers for changes to feature flags at this rate (in seconds).
3600 seconds
All
segmentsRefreshRate
The SDK polls Harness servers for changes to segments at this rate (in seconds).
1800 seconds
All
impressionsRefreshRate
Controls how frequently the impressions cache expires after a write (in seconds). The treatment log captures which customer saw which treatment (on, off, etc.) and is periodically flushed back to Harness servers.
1800 seconds
All
telemetryRefreshRate
The SDK caches diagnostic data that it periodically sends to Harness servers. This configuration controls how frequently this data is sent back to Harness servers (in seconds).
3600 seconds (1 hour)
All
eventsQueueSize
When using .track, the number of events to be kept in memory.
10000
All
eventFlushInterval
When using .track, how often is the events queue flushed to Harness servers.
1800 seconds
All
eventsPerPush
Maximum size of the batch to push events.
2000
Android & iOS
trafficType
When using .track, the default traffic type to be used.
not set
All
impressionsQueueSize
Default queue size for impressions.
30K
All
enableDebug
Enabled verbose mode.
false
All
streamingEnabled
Boolean flag to enable the streaming service as default synchronization mechanism when in foreground. In the event of an issue with streaming, the SDK will fallback to the polling mechanism. If false, the SDK will poll for changes as usual without attempting to use streaming.
true
All
persistentAttributesEnabled
Enables saving attributes on persistent cache which is loaded as part of the SDK_READY_FROM_CACHE flow. All functions that mutate the stored attributes map affect the persistent cache.
false
Android & iOS
impressionListener
Enables impression listener. If true, generated impressions stream in the impressionsStream() method of Splitio.
false
All
syncConfig
Use it to filter specific feature flags to be synced and evaluated by the SDK. It can be created with the SyncConfig.flagSets('sets') method (recommended, flag sets aree available in all tiers) or SyncConfig(names: ["feature-flag-1", "feature-flag-2"]) for individual names. If not set, all flags are downloaded.
not set
All
syncEnabled
Controls the SDK continuous synchronization flags. When true, a running SDK processes the rollout plan updates which is performed in Harness FME (default). When false, it fetches all data upon init, which ensures a consistent experience during a user session and optimizes resources when these updates are not consumed by the app.
true
All
userConsent
User consent status controls the tracking of events and impressions. Possible values are UserConsent.granted, UserConsent.decline, and UserConsent.unknown. See User consent for details.
UserConsent.granted
All
encryptionEnabled
Enables or disables encryption for cached data.
false
Android & iOS
logLevel
Enables logging according to the level specified. Options are SplitLogLevel.none, SplitLogLevel.verbose, SplitLogLevel.debug, SplitLogLevel.info, SplitLogLevel.warning, and SplitLogLevel.error.
SplitLogLevel.none
All
impressionsMode
This configuration defines how impressions (decisioning events) are queued. Supported modes are ImpressionsMode.optimized, ImpressionsMode.none, and ImpressionsMode.debug. In ImpressionsMode.optimized mode, only unique impressions are queued and posted to Harness; this is the recommended mode for experimentation use cases. In ImpressionsMode.none mode, no impression is tracked in Harness FME and only minimum viable data to support usage stats is, so never use this mode if you are experimenting with that instance impressions. Use ImpressionsMode.none when you want to optimize for feature flagging only use cases and reduce impressions network and storage load. In ImpressionsMode.debug mode, ALL impressions are queued and sent to Harness. This is useful for validations. This mode doesn't impact the impression listener which receives all generated impressions locally.
ImpressionsMode.optimized
All
readyTimeout
Maximum amount of time (in seconds) to wait until the onTimeout callback is fired or whenTimeout future is completed. A negative value means no timeout.
10 seconds
All
certificatePinningConfiguration
If set, enables certificate pinning for the given domains. For details, see the Certificate pinning section below.
null
Android & iOS
rolloutCacheConfiguration
Specifies how long rollout data is kept in local storage before expiring.
null
All
fallbackTreatments
Configure fallback treatments for the SDK.
null
All
Manager
Use these methods on Splitio instance to get a list of the feature flags available to the SDK factory client.
The SplitView class referenced above has the following structure:
Listener
FME SDKs send impression data back to Harness servers periodically as a result of evaluating feature flags. To additionally send this information to a location of your choice, use the impressionsStream.
This provides a stream that publishes Impression objects every time one is generated.
The Impression class has the following format.
An impression listener is called asynchronously from the corresponding evaluation, but is almost immediate.
Logging
To enable logging, the logLevel setting is available in the configuration class:
Configure fallback treatments
Fallback treatments let you define a treatment value (and optional configuration) to be returned when a flag cannot be evaluated. By default, the SDK returns control, but you can override this globally or for individual flags at the SDK level.
This is useful when you want to:
Maintain a predictable user experience during outages or evaluation failures (avoid unexpected
controlin production)Protect critical user flows by returning a safe, stable treatment (for example, forcing
offduring an incident)Customize behavior per flag so each evaluation inherits appropriate safe defaults if something goes wrong
Global fallback treatment
Set a global fallback treatment when initializing the SDK factory. This value is returned whenever any flag cannot be evaluated.
Flag-level fallback treatment
You can also set a fallback treatment per flag when calling getTreatment or getTreatmentWithConfig. This flag-level fallback always takes precedence over the global fallback treatment, so if both are defined, the SDK will return the flag-level value when that flag cannot be evaluated.
For more information, see Fallback treatments.
Advanced use cases
This section describes advanced use cases and features provided by the SDK.
Instantiate multiple SDK clients
FME supports the ability to release based on multiple traffic types. For example, with traffic types, you can release to users in one feature flag and accounts in another. If you are unfamiliar with using multiple traffic types, refer to the Traffic type guide for more information.
Each SDK factory client is tied to one specific customer ID at a time, so if you need to roll out feature flags by different keys, instantiate multiple SDK clients, one for each traffic type. For example, you may want to roll out the feature USER_POLL by users and the feature ACCOUNT_PERMISSIONING by accounts. You can do this with the example below:
Subscribe to events
You can subscribe to four different callbacks when creating a client.
onReadyFromCache. This event fires once the SDK is ready to evaluate treatments using a locally cached version of your rollout plan from a previous session (which might be stale). If there is data in the cache, this event fires almost immediately, since access to the cache is fast; otherwise, it doesn't fire.onReady. This event fires once the SDK is ready to evaluate treatments using the most up-to-date version of your rollout plan, downloaded from Harness servers.onTimeout. This event fires if there is no cached version of your rollout plan in disk cache, and the SDK could not fully download the data from Harness servers within the time specified by thereadyTimeoutconfiguration parameter. This event does not indicate that the SDK initialization was interrupted. The SDK continues downloading the rollout plan and fires theonReadyevent when finished. This delayedonReadyevent may happen with slow connections or large rollout plans with many feature flags, segments, or dynamic configurations.onUpdate. This event fires whenever your rollout plan is changed. Listen for this event to refresh your app whenever a feature flag or segment is changed in Harness FME.
You can also receive Futures (or a Stream, for the Update event) by accessing the following methods in the client.
User consent
The plugin allows you to disable the tracking of events and impressions until user consent is explicitly granted or declined.
The userConsent configuration parameter lets you set the initial consent status of the SDK, and the Splitio method setUserConsent(enabled: bool) lets you grant (enable) or decline (disable) the dynamic data tracking.
The following are the three possible initial states:
UserConsent.granted. The user grants consent for tracking events and impressions. The SDK sends them to the Harness FME servers. This is the default value if theuserConsentparam is not defined.UserConsent.declined. The user declines consent for tracking events and impressions. The SDK does not send them to the Harness FME servers.UserConsent.unknown. The user neither grants nor declines consent for tracking events and impressions. The SDK tracks them in its internal storage, and eventually either sends them or not if the consent status is updated to'GRANTED'or'DECLINED'respectively. The status can be updated at any time with thesetUserConsentfactory method.
Certificate pinning
PLATFORM SUPPORT
This feature is only supported on Android and iOS platforms.
The plugin allows you to constrain the certificates that it trusts, by pinning a certificate's SubjectPublicKeyInfo providing the public key as a base64 SHA-256 hash or a base64 SHA-1 hash.
Each pin corresponds to a host. For subdomains, you can optionally use wildcards, where * will match one subdomain (e.g. *.example.com), and ** will match any number of subdomains (e.g **.example.com).
To set the plugin to require pinned certificates for specific hosts, add the CertificatePinningConfiguration object to the configuration, as shown below.
Link with native factory
A native SplitFactory instance can be shared with the plugin to save resources when evaluations need to be performed on native platform logic. To do so, do the following:
Android
If not created already, create a subclass of Android's
Application, and add its name to the Manifest.
Add the Android SDK dependency to your project's
build.gradlefile.
Create a property in your subclass of
Applicationto hold your factory instance.Initialize the factory in the
onCreatecallback of yourApplicationsubclass.
Make the
Applicationsubclass implement theSplitFactoryProviderinterface, and return the previously created factory in the overriddengetSplitFactory()method.
iOS
Add the iOS SDK dependency to your app's
Podfile.
Add a property in your AppDelegate class to hold the factory instance. Make sure to import
Split.
Initialize the factory just before the
GeneratedPluginRegistrant.register(with: self)line.
Implement the
SplitFactoryProviderprotocol in yourAppDelegateand return the previously created factory in the overriddengetFactory()method.
WARNING
By using this method, all configuration declared when instantiating the Plugin in Flutter are ignored, since the factory is already instantiated and its configuration loaded.
Instantiating the factory natively prevents the plugin from setting up an Impression Listener, so impressions won't be accessible from Flutter. However, Impression Listeners can still be added and used in native code.
Last updated
Was this helpful?