From eac0305963e34c4b7020e69d172afd8423b9a823 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Fri, 26 Jun 2026 22:31:56 -0400 Subject: [PATCH 1/3] docs(feature-flags): add Dart client guide --- config/_default/menus/main.en.yaml | 15 +- content/en/feature_flags/client/_index.md | 3 + content/en/feature_flags/client/flutter.md | 367 ++++++++++++++++++ .../guide/migrate_from_launchdarkly.md | 1 + 4 files changed, 381 insertions(+), 5 deletions(-) create mode 100644 content/en/feature_flags/client/flutter.md diff --git a/config/_default/menus/main.en.yaml b/config/_default/menus/main.en.yaml index 914c6fea623..50b174623d3 100644 --- a/config/_default/menus/main.en.yaml +++ b/config/_default/menus/main.en.yaml @@ -6267,31 +6267,36 @@ menu: parent: feature_flags_client identifier: feature_flags_client_angular weight: 102 + - name: Dart + url: feature_flags/client/flutter + parent: feature_flags_client + identifier: feature_flags_client_flutter + weight: 103 - name: iOS and tvOS url: feature_flags/client/ios parent: feature_flags_client identifier: feature_flags_client_ios - weight: 103 + weight: 104 - name: JavaScript url: feature_flags/client/javascript parent: feature_flags_client identifier: feature_flags_client_javascript - weight: 104 + weight: 105 - name: React url: feature_flags/client/react parent: feature_flags_client identifier: feature_flags_client_react - weight: 105 + weight: 106 - name: React Native url: feature_flags/client/reactnative parent: feature_flags_client identifier: feature_flags_client_react_native - weight: 106 + weight: 107 - name: Unity url: feature_flags/client/unity parent: feature_flags_client identifier: feature_flags_client_unity - weight: 107 + weight: 108 - name: Server SDKs url: feature_flags/server parent: feature_flags diff --git a/content/en/feature_flags/client/_index.md b/content/en/feature_flags/client/_index.md index 51226aa5fbf..398e46833d4 100644 --- a/content/en/feature_flags/client/_index.md +++ b/content/en/feature_flags/client/_index.md @@ -25,6 +25,7 @@ Datadog Feature Flags is built on the [OpenFeature standard](https://openfeature {{< image-card href="/feature_flags/client/android/" src="integrations_logos/android_large.svg" alt="Android" >}} {{< image-card href="/feature_flags/client/android/" src="integrations_logos/android_tv_large.svg" alt="Android TV" >}} {{< image-card href="/feature_flags/client/angular/" src="integrations_logos/angular_large.svg" alt="Angular" >}} + {{< image-card href="/feature_flags/client/flutter/" src="integrations_logos/flutter_large.svg" alt="Dart" >}} {{< image-card href="/feature_flags/client/ios/" src="integrations_logos/ios_large.svg" alt="iOS" >}} {{< image-card href="/feature_flags/client/javascript/" src="integrations_logos/javascript_large.svg" alt="JavaScript" >}} {{< image-card href="/feature_flags/client/react/" src="integrations_logos/react_large.svg" alt="React" >}} @@ -45,6 +46,7 @@ Default: `true`. Set to `false` to disable. - **Web** (`@datadog/openfeature-browser`): `enableExposureLogging` - **Android** (`dd-sdk-android-flags`): `trackExposures` +- **Dart** (`datadog_flags`): `trackExposures` - **iOS** (`DatadogFlags`): `trackExposures` - **React Native**: `trackExposures` - **Unity**: `trackExposures` @@ -55,6 +57,7 @@ Default: `true`. Set to `false` to disable. - **Web** (`@datadog/openfeature-browser`): `enableFlagEvaluationTracking` - **Android** (`dd-sdk-android-flags`): `trackEvaluations` +- **Dart** (`datadog_flags`): `trackEvaluations` - **iOS** (`DatadogFlags`): `trackEvaluations` - **React Native**: Not exposed - **Unity**: `trackEvaluations` diff --git a/content/en/feature_flags/client/flutter.md b/content/en/feature_flags/client/flutter.md new file mode 100644 index 00000000000..b681354b829 --- /dev/null +++ b/content/en/feature_flags/client/flutter.md @@ -0,0 +1,367 @@ +--- +title: Dart Feature Flags +description: Set up Datadog Feature Flags for Dart applications. +further_reading: +- link: "/feature_flags/client/" + tag: "Documentation" + text: "Client-Side Feature Flags" +- link: "https://github.com/DataDog/dd-sdk-flutter/tree/develop/packages/datadog_flags" + tag: "Source Code" + text: "datadog_flags source code" +--- + +## Overview + +This page describes how to instrument a Dart application with the Datadog Feature Flags SDK. Datadog feature flags provide a unified way to remotely control feature availability in your app, experiment safely, and deliver new experiences with confidence. + +The Datadog Feature Flags SDK for Dart is a native Dart package. It fetches precomputed assignments from Datadog, evaluates typed flag values locally, and reports exposure and flag evaluation telemetry back to Datadog. Flutter applications can also use this package directly from Dart code. + +
This package provides an OpenFeature-compatible API for Dart, but it is not built on the OpenFeature Dart SDK. Datadog is working on an OpenFeature-provider-based integration for Dart and Flutter. Until that is available, use the APIs on this page directly.
+ +## Installation + +Install `datadog_flags`: + +{{< tabs >}} +{{% tab "Dart" %}} +{{< code-block lang="bash" >}} +dart pub add datadog_flags +{{< /code-block >}} +{{% /tab %}} + +{{% tab "Flutter" %}} +{{< code-block lang="bash" >}} +flutter pub add datadog_flags +{{< /code-block >}} +{{% /tab %}} +{{< /tabs >}} + +Then import the public API: + +{{< code-block lang="dart" >}} +import 'package:datadog_flags/datadog_flags.dart'; +{{< /code-block >}} + +## Initialize the SDK + +Enable Datadog Feature Flags early in your app startup. For live Feature Flags configuration, `clientToken`, `env`, and `site` are required. To create a client token, see [Client tokens][1]. + +{{< site-region region="gov,gov2" >}}
Dart Feature Flags are not supported for the selected Datadog site ({{< region-param key="dd_site_name" >}}).
{{< /site-region >}} + +{{< code-block lang="dart" >}} +final datadogFlags = DatadogFlags.instance; + +await datadogFlags.enable( + configuration: DatadogFlagsConfiguration( + datadogConfig: const DatadogFlagsConfig( + clientToken: '', + env: '', + site: DatadogFlagsSite.{{< region-param key="dd_datacenter_lowercase" code="true" >}}, + applicationId: '', + service: '', + version: '', + ), + ), +); +{{< /code-block >}} + +`applicationId`, `service`, and `version` are optional. When present, the SDK includes them in Feature Flags telemetry context. + +Use the `DatadogFlagsSite` value that matches your Datadog organization. + +## Create and retrieve a client + +Create or retrieve a shared client once during app startup: + +{{< code-block lang="dart" >}} +final flagsClient = DatadogFlags.instance.sharedClient(); +{{< /code-block >}} + +You can also create multiple named clients for independent evaluation contexts: + +{{< code-block lang="dart" >}} +final orgFlags = DatadogFlags.instance.sharedClient(name: 'org'); +final userFlags = DatadogFlags.instance.sharedClient(name: 'user'); +{{< /code-block >}} + +Clients are local to the Dart isolate where they are created. Background isolates do not share `DatadogFlags` state or assignment caches with the main isolate. If a background isolate needs to evaluate flags, call `DatadogFlags.instance.enable()`, create the clients it needs, and initialize them independently. + +## Set the evaluation context + +Define who or what the flag evaluation applies to using `FlagsEvaluationContext`. The evaluation context includes user, organization, session, or device information used to determine which flag variations should be returned. Call `initialize()` before evaluating flags so the client can fetch assignments for the context. + +
Datadog Feature Flags requires evaluation context attributes to be flat primitive values: strings, numbers, and Booleans. Do not pass nested objects or arrays; they are not supported and can cause exposure data to be dropped.
+ +{{< code-block lang="dart" >}} +await flagsClient.initialize( + const FlagsEvaluationContext( + targetingKey: 'user-123', + attributes: { + 'companyId': 'company-456', + 'plan': 'enterprise', + 'loggedIn': true, + }, + ), +); +{{< /code-block >}} + +The `targetingKey` is the randomization subject for percentage rollouts. Users with the same targeting key always receive the same variant for a given flag. + +`targetingKey` is optional. If you initialize a context before a user or organization ID is known, the SDK sends an empty string for the precompute assignment request. + +Use separate named clients for separate mobile subjects, such as logged-out and logged-in users or org-level and user-level targeting: + +{{< code-block lang="dart" >}} +await orgFlags.initialize( + const FlagsEvaluationContext(targetingKey: 'org-123'), +); + +await userFlags.initialize( + const FlagsEvaluationContext(targetingKey: 'user-456'), +); +{{< /code-block >}} + +## Evaluate flags + +After a client is initialized, you can read flag values throughout your app. Flag evaluation is _local and instantaneous_ because the SDK uses locally cached assignment data. No network request occurs during a typed evaluation. + +Each evaluation method requires a caller-provided default value. Evaluation methods do not throw for provider readiness, missing flags, or type mismatches. They return a `FlagDetails` value with the evaluated value, assignment metadata, and a programmatic error when the SDK returns the default. + +### Boolean flags + +Use `getBooleanDetails()` for flags that represent on/off or true/false conditions: + +{{< code-block lang="dart" >}} +final details = flagsClient.getBooleanDetails( + key: 'checkout.enabled', + defaultValue: false, +); + +if (details.error == null && details.value) { + showNewCheckoutFlow(); +} else { + showLegacyCheckout(); +} +{{< /code-block >}} + +### String flags + +Use `getStringDetails()` for flags that select between multiple variants or configuration strings: + +{{< code-block lang="dart" >}} +final details = flagsClient.getStringDetails( + key: 'ui.theme', + defaultValue: 'light', +); + +if (details.value == 'dark') { + setDarkTheme(); +} else { + setLightTheme(); +} +{{< /code-block >}} + +### Integer and double flags + +Use `getIntegerDetails()` or `getDoubleDetails()` for numeric flags, such as limits, percentages, or multipliers: + +{{< code-block lang="dart" >}} +final maxItems = flagsClient.getIntegerDetails( + key: 'cart.items.max', + defaultValue: 20, +); + +final priceMultiplier = flagsClient.getDoubleDetails( + key: 'pricing.multiplier', + defaultValue: 1.0, +); +{{< /code-block >}} + +### Object flags + +Use `getObjectDetails()` for JSON-compatible structured configuration: + +{{< code-block lang="dart" >}} +final config = flagsClient.getObjectDetails( + key: 'ui.config', + defaultValue: const { + 'color': '#00A3FF', + 'fontSize': 14, + }, +); +{{< /code-block >}} + +### Flag evaluation details + +Use the details APIs when you need the evaluated value, variant, reason, or evaluation error: + +{{< code-block lang="dart" >}} +final details = flagsClient.getStringDetails( + key: 'checkout.copy', + defaultValue: 'Continue', +); + +print(details.value); +print(details.variant); +print(details.reason); +print(details.error?.code); +{{< /code-block >}} + +`FlagDetails.error` is set when the SDK returns the default value because the provider is not ready, the flag is not found, or the assignment value does not match the typed evaluation method. Successful details include the evaluated value plus assignment metadata, such as `variant` and `reason`, when Datadog returned it. + +## Advanced configuration + +`DatadogFlagsConfiguration` controls SDK behavior: + +{{< code-block lang="dart" >}} +DatadogFlagsConfiguration( + datadogConfig: datadogConfig, + trackExposures: true, + trackEvaluations: true, + evaluationFlushInterval: const Duration(seconds: 10), + store: myStore, +); +{{< /code-block >}} + +`trackExposures` +: When `true` (default), the SDK records exposure events for successful evaluations whose assignments are marked for logging. Set to `false` to disable exposure tracking. + +`trackEvaluations` +: When `true` (default), the SDK records aggregated flag evaluation telemetry. Set to `false` to disable evaluation tracking. + +`evaluationFlushInterval` +: The interval at which aggregated flag evaluation telemetry is sent to Datadog. Accepted values are between 1 and 60 seconds. The default is 10 seconds. + +`store` +: Optional last-known assignment storage. The SDK can use matching stored assignments while a fresh network request is in progress or unavailable. + +`httpClient`, `customFlagsEndpoint`, `customExposureEndpoint`, and `customEvaluationEndpoint` +: Advanced overrides for tests, proxies, or custom routing. + +If `enable()` is called without a `datadogConfig`, the SDK creates no live provider. Evaluations return the caller-provided default with `FlagEvaluationError.providerNotReady`. + +## Last-known assignment storage + +The SDK keeps assignments in memory after `initialize()` succeeds. To restore last-known assignments across SDK instances, provide a `DatadogFlagsStore`: + +{{< code-block lang="dart" >}} +class MyFlagsStore implements DatadogFlagsStore { + @override + Future read(String clientName) async { + // Read and decode persisted FlagsData for this client name. + return null; + } + + @override + Future write(String clientName, FlagsData data) async { + // Encode and persist successful assignments for this client name. + } + + @override + Future delete(String clientName) async { + // Delete persisted assignments for this client name. + } +} +{{< /code-block >}} + +Stored assignments are used only when their evaluation context matches the active context. A live successful fetch always moves the client to the newest assignment state and writes that state back to the store. + +The Dart package does not choose a disk location or ship a Flutter-specific disk store. Flutter apps can implement `DatadogFlagsStore` with their preferred app storage mechanism. + +## Shutdown + +Call `shutdown()` when a client is no longer needed. This drains pending exposure and flag evaluation uploads before clearing the client's in-memory assignments. + +{{< code-block lang="dart" >}} +await flagsClient.shutdown(); +{{< /code-block >}} + +Call `DatadogFlags.instance.disable()` when the application is tearing down the flags SDK: + +{{< code-block lang="dart" >}} +await DatadogFlags.instance.disable(); +{{< /code-block >}} + +## Complete example + +The following example enables the SDK, initializes a client with an evaluation context, and evaluates a Boolean flag: + +{{< code-block lang="dart" >}} +import 'package:datadog_flags/datadog_flags.dart'; + +Future initializeFlags() async { + final datadogFlags = DatadogFlags.instance; + + await datadogFlags.enable( + configuration: DatadogFlagsConfiguration( + datadogConfig: const DatadogFlagsConfig( + clientToken: '', + env: '', + site: DatadogFlagsSite.{{< region-param key="dd_datacenter_lowercase" code="true" >}}, + applicationId: '', + service: '', + version: '', + ), + ), + ); + + final flagsClient = datadogFlags.sharedClient(); + await flagsClient.initialize( + const FlagsEvaluationContext( + targetingKey: 'user-123', + attributes: { + 'companyId': 'company-456', + 'plan': 'enterprise', + }, + ), + ); + + final details = flagsClient.getBooleanDetails( + key: 'checkout.enabled', + defaultValue: false, + ); + + if (details.error == null && details.value) { + showNewCheckoutFlow(); + } +} +{{< /code-block >}} + +## Testing + +You can test against a dedicated Datadog test environment with the real `DatadogFlagsClient`, or isolate application code behind a small interface and substitute a fake implementation in unit tests. This section shows the fake approach, which keeps tests hermetic and offline. + +{{< code-block lang="dart" >}} +abstract interface class CheckoutFlags { + bool newCheckoutEnabled(); +} + +final class DatadogCheckoutFlags implements CheckoutFlags { + final DatadogFlagsClient client; + + DatadogCheckoutFlags(this.client); + + @override + bool newCheckoutEnabled() { + return client + .getBooleanDetails( + key: 'checkout.enabled', + defaultValue: false, + ) + .value; + } +} + +final class TestCheckoutFlags implements CheckoutFlags { + @override + bool newCheckoutEnabled() => true; +} +{{< /code-block >}} + +Then inject `TestCheckoutFlags` into unit tests and `DatadogCheckoutFlags` in production. + +## Further reading + +{{< partial name="whats-next/whats-next.html" >}} + +[1]: /account_management/api-app-keys/#client-tokens diff --git a/content/en/feature_flags/guide/migrate_from_launchdarkly.md b/content/en/feature_flags/guide/migrate_from_launchdarkly.md index 4de10d1795a..ccd503fe44a 100644 --- a/content/en/feature_flags/guide/migrate_from_launchdarkly.md +++ b/content/en/feature_flags/guide/migrate_from_launchdarkly.md @@ -39,6 +39,7 @@ Follow the installation instructions for your platform: {{< image-card href="/feature_flags/client/android/" src="integrations_logos/android_large.svg" alt="Android" >}} {{< image-card href="/feature_flags/client/android/" src="integrations_logos/android_tv_large.svg" alt="Android TV" >}} {{< image-card href="/feature_flags/client/angular/" src="integrations_logos/angular_large.svg" alt="Angular" >}} + {{< image-card href="/feature_flags/client/flutter/" src="integrations_logos/flutter_large.svg" alt="Dart" >}} {{< image-card href="/feature_flags/client/ios/" src="integrations_logos/ios_large.svg" alt="iOS" >}} {{< image-card href="/feature_flags/client/javascript/" src="integrations_logos/javascript_large.svg" alt="JavaScript" >}} {{< image-card href="/feature_flags/client/react/" src="integrations_logos/react_large.svg" alt="React" >}} From 6e1276b421a664ff602f77b55a9f90d047902fd5 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Sun, 28 Jun 2026 19:57:51 -0400 Subject: [PATCH 2/3] Update content/en/feature_flags/client/flutter.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- content/en/feature_flags/client/flutter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/en/feature_flags/client/flutter.md b/content/en/feature_flags/client/flutter.md index b681354b829..18f1073fa03 100644 --- a/content/en/feature_flags/client/flutter.md +++ b/content/en/feature_flags/client/flutter.md @@ -109,7 +109,7 @@ The `targetingKey` is the randomization subject for percentage rollouts. Users w `targetingKey` is optional. If you initialize a context before a user or organization ID is known, the SDK sends an empty string for the precompute assignment request. -Use separate named clients for separate mobile subjects, such as logged-out and logged-in users or org-level and user-level targeting: +Use separate named clients for separate evaluation subjects, such as logged-out and logged-in users or org-level and user-level targeting: {{< code-block lang="dart" >}} await orgFlags.initialize( From 3821821b3dbb5a6c3c9da00f4126f40567acfc09 Mon Sep 17 00:00:00 2001 From: Leo Romanovsky Date: Sun, 28 Jun 2026 19:58:10 -0400 Subject: [PATCH 3/3] Update content/en/feature_flags/client/flutter.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- content/en/feature_flags/client/flutter.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/en/feature_flags/client/flutter.md b/content/en/feature_flags/client/flutter.md index 18f1073fa03..96da9443bfc 100644 --- a/content/en/feature_flags/client/flutter.md +++ b/content/en/feature_flags/client/flutter.md @@ -16,7 +16,7 @@ This page describes how to instrument a Dart application with the Datadog Featur The Datadog Feature Flags SDK for Dart is a native Dart package. It fetches precomputed assignments from Datadog, evaluates typed flag values locally, and reports exposure and flag evaluation telemetry back to Datadog. Flutter applications can also use this package directly from Dart code. -
This package provides an OpenFeature-compatible API for Dart, but it is not built on the OpenFeature Dart SDK. Datadog is working on an OpenFeature-provider-based integration for Dart and Flutter. Until that is available, use the APIs on this page directly.
+
This package provides an OpenFeature-compatible API for Dart, but it is not built on the OpenFeature Dart SDK. Use the APIs on this page directly.
## Installation