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 userId for stable rollout buckets) updates immediately, with targeting rules re-evaluated against the new attributes. The SDK persists the latest snapshot to local storage via shared_preferences, so a cold start in airplane mode still resolves flags from the last successful sync rather than falling back to defaults.
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 |
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). 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,});flush()
Section titled “flush()”Forces all buffered events to be sent immediately.
await client.flush();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