Skip to content

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

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

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

Conversation

@typotter

@typotter typotter commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

What and why?

Applications need to react when the first usable flag configuration is installed, including cached flags available before network initialization finishes. Add onFirstFlags directly to the Dart and Flutter flags clients:

typedef FlagsClientEventListener = void Function(FlagsClientEvent event);
void Function() onFirstFlags(FlagsClientEventListener listener);

Each registration receives the first accepted configuration's immutable key inventory once, including an empty list for an empty configuration. Late registrations receive the retained event. If no usable configuration is installed, the registration can remain pending until cancelled. Evaluations in the callback read current flag values.

This is a source-breaking interface addition: custom DatadogFlagsClient implementations must implement the method. The core and Flutter package release versions and minimum dependency must be coordinated before publication.

How?

Core retains the first event, binds listeners to their registration zones and schedules delivery in a later microtask. The returned unregister function is idempotent and suppresses delivery until the callback starts. Callback references are cleared on cancellation or delivery. Synchronous listener errors are isolated; the application owns errors from asynchronous work started by its callback.

Flutter resolves its underlying client and forwards registration, delivery and cancellation. Cancellation also works while that resolution is pending. FlagsClientEvent keeps its constructor annotated @internal and defensively copies its key list.

The CLI example prints the installed keys and evaluates its selected flag. The Flutter example registers in initState, displays a callback evaluation and unregisters in dispose; it uses the repository's .env convention.

The Flutter app registers and cleans up as follows (exact excerpt):

@override
void initState() {
  super.initState();
  final client = DatadogSdk.instance.flags?.sharedClient();
  if (widget.isConfigured && client != null) {
    _unregisterFirstFlags = client.onFirstFlags((event) {
      debugPrint('First installed flags: ${event.flagsChanged}');
      final details = client.getBooleanDetails(
        key: _flagKey,
        defaultValue: false,
      );
      if (mounted) setState(() => _firstDetails = details);
    });
  }
  unawaited(_evaluate());
}

@override
void dispose() {
  _unregisterFirstFlags?.call();
  super.dispose();
}

Local validation: 123 core tests and 23 Flutter tests passed, including controlled microtask/resolver tests and real-core Flutter RUM integration. Both packages and examples analyse cleanly; 49 Dart files pass formatting checks. The CLI executable and Flutter debug bundle build successfully. These checks use companion source packages and do not establish native-device or published-package compatibility.

Review checklist

  • This pull request has appropriate unit and / or integration tests
  • This pull request references a Github or JIRA issue

@datadog-datadog-prod-us1-2

This comment has been minimized.

@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
@typotter
typotter requested a balanced review from Copilot October 6, 2026 20:18

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 previously approved these changes Oct 6, 2026

@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.

@fuzzybinary fuzzybinary left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The two very big things here that I'm concerned about:

  • Calling the GC directly, even in tests. Unless I'm mistaken, that is not publicly documented, and we have no control over what Dart does with that call, or even what type of GC it might trigger.
  • The additions both README are way too detailed for public facing documentation and in some cases are specifically instructions to us as maintainers. If we want to retain that information, it needs to go somewhere else.

Comment thread packages/datadog_flags/example/test/typed_evaluation_test.dart Outdated
Comment thread packages/datadog_flags/lib/src/default_flags_client.dart Outdated
Comment thread packages/datadog_flags/lib/src/flags_client.dart Outdated
Comment thread packages/datadog_flags/lib/src/flags_client_event.dart Outdated
Comment thread packages/datadog_flags/test/first_flags_registration_test.dart Outdated
Comment thread packages/datadog_flags/CHANGELOG.md Outdated
Comment thread packages/datadog_flags/README.md Outdated
Comment thread packages/datadog_flags/README.md Outdated
Comment thread packages/datadog_flags_flutter/example/test/first_flags_example_test.dart Outdated
Comment thread packages/datadog_flags_flutter/README.md Outdated
@barboraplasovska
barboraplasovska removed their request for review October 9, 2026 13:38
Notify applications when the first usable flag configuration is installed. Core retains the immutable key inventory and delivers callbacks asynchronously in their registration zones; Flutter bridges registration and cancellation to core.

Validate with 148 core and Flutter tests, static analysis, formatting, and compiled CLI and Flutter examples.

BREAKING CHANGE: Custom DatadogFlagsClient implementations must implement onFirstFlags.
@typotter
typotter force-pushed the codex/flutter-first-flags-retained branch from 7a70427 to 852b843 Compare October 9, 2026 20:49
@typotter typotter changed the title feat: add retained first flags callbacks directly to Dart and Flutter clients feat(flags): add retained first-flags callbacks to Dart and Flutter clients Oct 9, 2026
Updated documentation for onFirstFlags method to clarify behavior.
Updated documentation for FlagsClientEvent constructor.
Use FlagsClientEventListener across core and Flutter callback signatures while preserving inline closure compatibility and unregister behaviour.

Validated with 125 core tests, 23 Flutter tests, static analysis, formatting, and public-import checks for both packages.
Retain the source-list mutation check and remove redundant container and collection assertions. First-flags behavioural coverage remains unchanged.

Validation: 123 core tests pass; static analysis and formatting pass.
@typotter
typotter requested a review from fuzzybinary October 14, 2026 15:25
@typotter

Copy link
Copy Markdown
Contributor Author

Thanks for the detailed review, Jeff. PTAL

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.

6 participants