Skip to content

feat: add retained first flags callbacks directly to Dart and Flutter clients - #1204

Open
typotter wants to merge 8 commits into
developfrom
codex/flutter-first-flags-retained
Open

typotter wants to merge 8 commits into
developfrom
codex/flutter-first-flags-retained

Conversation

@typotter

@typotter typotter commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Applications can register for the first accepted flags installation directly on the flags client, including after that installation has completed.

void Function() onFirstFlags(void Function(FlagsClientEvent event) listener);

Each registration receives the retained first event once and returns an idempotent unregister function. The callback receives only FlagsClientEvent, with type and nullable immutable flagsChanged; the implemented type is CONFIGURATION_CHANGED. Delivery always runs in a microtask in the registration zone. Cancellation releases captures and suppresses delivery until the callback starts, including while the Flutter delegate is resolving. The event contains all keys of the first accepted configuration, not one notification per flag or all flags on the server. Valid empty configurations notify with []; rejected/failed/undecodable loads do not consume the signal. Registration and retained-event replay perform no SDK I/O. Evaluations read current assignments, not a snapshot pinned to the event; existing failed-fetch fallback behavior is unchanged.

The API is a member of DatadogFlagsClient, implemented directly by the core, no-op, and Flutter clients. There is no events facade, extension, registry, or runtime capability lookup. The event constructor is annotated @internal: it is excluded from generated API docs and external construction receives an analyzer warning. It remains in its existing library; there is no new public builder/factory. The copied List.unmodifiable payload is preserved.

This is a source-breaking interface addition: custom implementations and test doubles must implement the method; decorators can forward it to their delegate. The SDK no-op client accepts registrations without emitting events.

The real CLI example registers with:

final flags = datadogFlags.sharedClient();
final unregister = flags.onFirstFlags((event) {
  stdout.writeln('First installed flags: ${event.flagsChanged}');
  _printDetails(_evaluate(flags, flagKey, flagType));
});

The real Flutter app registers in initState and unregisters in dispose:

_unregisterFirstFlags = client.onFirstFlags((event) {
  debugPrint('First installed flags: ${event.flagsChanged}');
  final details = client.getBooleanDetails(
    key: _flagKey,
    defaultValue: false,
  );
  if (mounted) setState(() => _firstDetails = details);
});

Flutter forwards each registration once to the resolved core client. Registrations do not migrate across SDK re-enable; applications reacquire the shared client. Cache admission, CACHED provenance, evaluation telemetry, retained-event ownership, and exception isolation remain unchanged. Callback logging and failed-fetch retention changes are outside this PR.

CACHED #1203 is merged; this PR targets develop. The wrapper requires datadog_flags: ^1.2.0 for CACHED support. Before publishing the integration, publish core containing this direct API and ensure the wrapper minimum selects that release. Local path overrides prove companion-source compatibility, not registry availability. Release versions remain coordinated separately.

Validation on the direct-client implementation: 157 tests passed (125 core, 24 Flutter wrapper including actual-core forwarding/RUM integration, four forced-GC capture-release tests, two CLI example tests, and two actual Flutter app tests). Both packages and both examples analyze cleanly; 56 Dart files pass formatting. Both packages generate API documentation with zero warnings/errors and no public event constructor. External probes through both barrels confirm invalid_use_of_internal_member for construction and clean analysis for callback consumption. Core requires meta: ^1.3.0, where @internal was introduced. The CLI executable/help and Flutter debug bundle build pass. Coverage includes isolated failed-load/valid-empty paths, cache-empty delivery before network completion, defensive source aliasing, retained keys versus current values after successful/failed context updates, replay with no fetch/persistence I/O, reentrant progress, synchronous Exception/Error/other thrown-object isolation, zone-value/async-error routing, microtask ordering, and independent unregister. Custom implementor fixtures compile through both public barrels. Facade-only tests were removed with the facade; callback capture-release coverage remains.

The Flutter app tests use a dummy token and mocked HTTP. Native-device execution, live Datadog behavior, and current-head remote CI are not established by these local checks.

@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Pipelines

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 7a70427 | Docs | View more details | Give us feedback!

@typotter
typotter marked this pull request as ready for review October 5, 2026 16:53
@typotter
typotter requested review from a team as code owners October 5, 2026 16:53
@typotter
typotter requested review from sameerank and vjfridge and removed request for a team October 5, 2026 16:53
@drichards-87 drichards-87 self-assigned this Oct 5, 2026
@typotter
typotter requested review from leoromanovsky and removed request for vjfridge October 5, 2026 18:17
drichards-87
drichards-87 previously approved these changes Oct 5, 2026
@drichards-87 drichards-87 removed their assignment Oct 5, 2026
@typotter
typotter requested a balanced review from Copilot October 5, 2026 18:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Cross-package asynchronous lifecycle, weak-reference, and cancellation semantics warrant final human validation despite extensive coverage.

Review effort: Balanced
Findings: None

What changed in this PR

Adds retained first-install flag callbacks across Dart core and Flutter, including cancellation, documentation, examples, and comprehensive tests.

Changes:

  • Adds immutable flag events and an event facade backed by weak client associations.
  • Implements one-shot asynchronous delivery and cancellation in core and Flutter.
  • Updates examples, documentation, and lifecycle/GC test coverage.
File Description
packages/​datadog_flags/​lib/​datadog_flags.dart Exports event APIs.
packages/​datadog_flags/​lib/​datadog_flags_internal.dart Exports the companion-package bridge.
packages/​datadog_flags/​lib/​src/​default_flags_client.dart Registers core event delivery.
packages/​datadog_flags/​lib/​src/​flags_client.dart Clarifies initialization documentation.
packages/​datadog_flags/​lib/​src/​flags_client_event.dart Defines immutable event data.
packages/​datadog_flags/​lib/​src/​flags_event_registry.dart Adds weak event-source associations.
packages/​datadog_flags/​lib/​src/​flags_events.dart Adds the public facade and extension.
packages/​datadog_flags/​lib/​src/​flags_repository.dart Retains and delivers the first event.
packages/​datadog_flags/​lib/​src/​no_op_flags_client.dart Supports no-op registration.
packages/​datadog_flags/​test/​flags_events_compatibility_test.dart Tests legacy-client compatibility.
packages/​datadog_flags/​test/​flags_client_event_test.dart Tests event immutability and type mapping.
packages/​datadog_flags/​test/​first_flags_registration_test.dart Tests registration and cancellation.
packages/​datadog_flags/​test/​first_flags_callback_test.dart Tests installation callback behavior.
packages/​datadog_flags/​test_vm/​helpers/​force_gc.dart Adds VM garbage-collection support.
packages/​datadog_flags/​test_vm/​first_flags_capture_test.dart Tests capture release and facade lifetime.
packages/​datadog_flags/​example/​bin/​typed_evaluation.dart Demonstrates first-install callbacks.
packages/​datadog_flags/​example/​test/​typed_evaluation_test.dart Tests the CLI example.
packages/​datadog_flags/​example/​pubspec.yaml Adds example test dependencies.
packages/​datadog_flags/​README.md Documents the core API.
packages/​datadog_flags/​CHANGELOG.md Records the core feature.
packages/​datadog_flags_flutter/​lib/​datadog_flags_flutter.dart Re-exports event APIs.
packages/​datadog_flags_flutter/​lib/​src/​datadog_flags_plugin.dart Forwards registrations to core.
packages/​datadog_flags_flutter/​test/​helpers/​first_flags_test_client.dart Adds a wrapper test factory.
packages/​datadog_flags_flutter/​test/​flags_events_compatibility_test.dart Tests Flutter compatibility.
packages/​datadog_flags_flutter/​test/​first_flags_registration_test.dart Tests forwarding and cancellation.
packages/​datadog_flags_flutter/​test_vm/​helpers/​force_gc.dart Adds Flutter VM GC support.
packages/​datadog_flags_flutter/​test_vm/​first_flags_capture_test.dart Tests wrapper capture release.
packages/​datadog_flags_flutter/​example/​lib/​main.dart Demonstrates callback lifecycle handling.
packages/​datadog_flags_flutter/​example/​test/​first_flags_example_test.dart Tests the Flutter example.
packages/​datadog_flags_flutter/​example/​pubspec.yaml Adds example test dependencies.
packages/​datadog_flags_flutter/​README.md Documents Flutter behavior and release coordination.
packages/​datadog_flags_flutter/​CHANGELOG.md Records the Flutter feature.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

sameerank
sameerank previously approved these changes Oct 5, 2026

@sameerank sameerank left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex found one correctness issue but I assume it's non-blocking. Doesn't sound common to register and initialize in different Dart zones

Comment thread packages/datadog_flags/lib/src/flags_repository.dart
Base automatically changed from typo/flutter-cached-reason to develop October 5, 2026 20:09
@gh-worker-dd-mergequeue-cf854d
gh-worker-dd-mergequeue-cf854d Bot dismissed stale reviews from sameerank and drichards-87 October 5, 2026 20:09

The base branch was changed.

@typotter typotter changed the title feat: retain first flags events for Dart and Flutter client callbacks feat: add retained first flags callbacks directly to Dart and Flutter clients Oct 6, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The source-breaking cross-package API and deferred release-version coordination require final human validation.

Review effort: Balanced
Findings: None

@leoromanovsky leoromanovsky left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't find any actionable defects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants