Flutter SDK Reference
TL;DR: Add
featurefliptopubspec.yaml, callclient.boolVariation('flag-key', defaultValue: false), and you’re evaluating flags from any Flutter app.
The featureflip Flutter package is a single client SDK for evaluating feature flags across every Flutter platform — iOS, Android, web, macOS, Windows, and Linux. It is a client-side SDK: flag values arrive pre-evaluated from the Featureflip Evaluation API, so your targeting rules stay on the server and never get bundled into your release IPA, AAB, or web build. Distribution is pub.dev as featureflip, with automated publishing from the canonical mirror repo via OIDC.
The SDK is designed around Dart Streams and the ValueListenable pattern, so it integrates cleanly with StreamBuilder, ValueListenableBuilder, Provider, riverpod, bloc, and get_it. A FeatureflipBuilder widget rebuilds your subtree whenever a flag value changes — useful when percentage rollouts shift assignments while the app is running. The streaming connection is paused automatically when the app goes to the background (via WidgetsBindingObserver) and resumed when it returns to the foreground, conserving battery and avoiding unnecessary network use on mobile.
Identification works the way you’d expect: call identify() after sign-in, and the active context (anchored on user_id for stable rollout buckets) updates immediately, with targeting rules re-evaluated against the new attributes. Flag values are held in memory, so a cold start with no network serves your defaults until the first fetch succeeds. The one thing the SDK writes to shared_preferences is the anonymous ID it generates for a signed-out user, which keeps that user in the same rollout bucket across launches.
Requirements
Section titled “Requirements”- Flutter 3.24+
- Dart 3.5+
Installation
Section titled “Installation”Add to your pubspec.yaml:
dependencies: featureflip: ^2.0.0Then run:
flutter pub getConfiguration
Section titled “Configuration”The FeatureflipConfig class accepts the following options:
| Option | Type | Default | Description |
|---|---|---|---|
clientKey | String | required | Client SDK key from your project settings |
baseUrl | String | "https://eval.featureflip.io" | Evaluation API base URL |
context | Map<String, dynamic> | {} | Initial evaluation context (user attributes) |
streaming | bool | true | Enable SSE for real-time flag updates |
pollIntervalSeconds | int | 30 | Polling interval in seconds (used when streaming is disabled) |
flushIntervalSeconds | int | 30 | Interval in seconds between event flush batches |
flushBatchSize | int | 100 | Maximum number of events per flush batch |
initTimeoutSeconds | int | 10 | Maximum time in seconds to wait for initial flag fetch |
sendEvaluationEvents | bool | true | Report which flags the app reads, so flags it no longer reads can be archived. See Read reporting |
wrapperName | String? | null | For libraries that wrap the SDK: a name sent after the SDK’s own identity token |
wrapperVersion | String? | null | Version sent with wrapperName |
Example configuration
Section titled “Example configuration”final config = FeatureflipConfig( clientKey: 'your-client-sdk-key', context: {'user_id': 'u-123'}, streaming: true, pollIntervalSeconds: 60, initTimeoutSeconds: 15,);Initialization
Section titled “Initialization”Obtain a client via the get() static factory and call initialize():
import 'package:featureflip/featureflip.dart';
final config = FeatureflipConfig(clientKey: 'your-client-sdk-key');final client = FeatureflipClient.get('your-client-sdk-key', config: config);await client.initialize();initialize() fetches flag values from the API, starts SSE streaming (or polling), and begins the event processor. The SDK is usable after initialization completes, even if the network request fails (flags default to provided defaults).
Lifetime
Section titled “Lifetime”FeatureflipClient.get() is singleton by construction — calling it multiple times with the same SDK key returns handles that share one underlying connection (refcounted). The connection shuts down only when the last handle is closed. This makes the client safe for any DI registration lifetime (singleton, scoped, or transient).
final client1 = FeatureflipClient.get('key', config: config);final client2 = FeatureflipClient.get('key', config: config);// Both share one connection. close() decrements the refcount.isInitialized
Section titled “isInitialized”client.isInitialized; // boolA read-only property that returns true once initialize() has completed (regardless of whether the initial network fetch succeeded), and false after close() — a closed handle serves the caller’s default from every accessor. For test clients created with forTesting(), this is immediately true.
Evaluating Flags
Section titled “Evaluating Flags”All variation methods are synchronous and return immediately from an in-memory cache. They accept a flag key and a default value. The default is returned if the flag is missing or has an unexpected type.
Since this is a client-side SDK, evaluation context is set at initialization or via identify() — it is not passed per evaluation call.
boolVariation(key, defaultValue:)
Section titled “boolVariation(key, defaultValue:)”final enabled = client.boolVariation('dark-mode', defaultValue: false);stringVariation(key, defaultValue:)
Section titled “stringVariation(key, defaultValue:)”final color = client.stringVariation('button-color', defaultValue: 'blue');numberVariation(key, defaultValue:)
Section titled “numberVariation(key, defaultValue:)”final limit = client.numberVariation('rate-limit', defaultValue: 100.0);Returns a double.
jsonVariation(key, defaultValue:)
Section titled “jsonVariation(key, defaultValue:)”final config = client.jsonVariation('ui-config', defaultValue: {'theme': 'light'});Returns the raw dynamic value.
Identifying Users
Section titled “Identifying Users”Call identify() to re-evaluate all flags for a new user context — typically after login. This makes a network request to the evaluation API with the new context and updates all cached flag values.
await client.identify({'user_id': 'u-123', 'plan': 'pro'});The streaming or polling data source is also updated with the new context.
Event Tracking
Section titled “Event Tracking”track(eventName, metadata:)
Section titled “track(eventName, metadata:)”Enqueues a custom analytics event. Events are batched and flushed automatically.
client.track('purchase-completed', metadata: { 'plan': 'pro', 'amount': 29.99,});Featureflip counts each event name, per environment. The metadata you pass is transmitted with the event but is not stored, and event counts are not surfaced in the app or API.
flush()
Section titled “flush()”Forces all buffered events to be sent immediately.
await client.flush();Read reporting
Section titled “Read reporting”Since 2.7.0, the SDK reports the flags your app reads, which is how Featureflip tells a client-side flag still in use from one whose code is gone. boolVariation, stringVariation, numberVariation and jsonVariation count, whether you call them on the client or on flagProvider. Reading flags in build() is fine. Each flag, variation and user is reported about once an hour per device and again whenever the app returns to the foreground, in the same batches as track() events.
allFlags() is the exception. A flag your app reaches only through it looks unused, so it can be marked stale and archived.
To opt out, pass sendEvaluationEvents: false, and Featureflip counts every flag it sends the app as read, so none of them can be archived while the app is in use.
Cleanup
Section titled “Cleanup”close()
Section titled “close()”Decrements the refcount on the shared connection. If this is the last handle for the SDK key, stops streaming or polling and flushes remaining events. Safe to call multiple times — subsequent calls are no-ops.
await client.close();Lifecycle management
Section titled “Lifecycle management”The SDK automatically observes app lifecycle events via WidgetsBindingObserver:
- Background (paused): Stops streaming/polling and flushes events
- Foreground (resumed): Resumes streaming/polling
No manual intervention is required.
Flutter Widget Integration
Section titled “Flutter Widget Integration”The SDK provides a FeatureflipProvider (a ChangeNotifier) for reactive Flutter widget updates. Access it via the client’s flagProvider property.
ListenableBuilder( listenable: client.flagProvider, builder: (context, _) { final showBanner = client.flagProvider.boolVariation( 'show-banner', defaultValue: false, ); if (!showBanner) return const SizedBox.shrink(); return const Text('New feature available!'); },)The provider notifies listeners when flag values change via streaming or identify().
Testing
Section titled “Testing”FeatureflipClient.forTesting(overrides)
Section titled “FeatureflipClient.forTesting(overrides)”Creates a test client with static flag overrides. No network calls are made and no background tasks are started. The client is immediately initialized.
final client = FeatureflipClient.forTesting({ 'dark-mode': true, 'pricing-tier': 'enterprise', 'rate-limit': 500,});
client.boolVariation('dark-mode', defaultValue: false); // trueclient.stringVariation('pricing-tier', defaultValue: 'free'); // 'enterprise'client.boolVariation('unknown', defaultValue: false); // false (default)Signature:
static FeatureflipClient forTesting(Map<String, dynamic> overrides)Supported override value types: bool, String, int, double.
FlagValue
Section titled “FlagValue”The internal representation of a flag returned by the server:
| Property | Type | Description |
|---|---|---|
value | dynamic | The evaluated flag value |
variation | String | The key of the matched variation |
reason | String | Why this value was returned |
Flag reasons
Section titled “Flag reasons”| Value | Description |
|---|---|
"Fallthrough" | No rules matched; the fallthrough variation was served |
"RuleMatch" | A targeting rule matched the context |
"FlagDisabled" | The flag is disabled; the off variation was served |
"FlagNotFound" | The flag key does not exist |
"Error" | An error occurred during evaluation |
"TEST" | Returned by test clients |
Platform Support
Section titled “Platform Support”- iOS
- Android
- Web
- macOS
- Linux
- Windows
See also
Section titled “See also”- Flutter Quickstart — get started in under 5 minutes
- Targeting & Segments — control which users see which variation
- Evaluation API — REST API reference
- SDK Overview — compare server-side and client-side SDKs