Skip to content
Back to archipelago.banuacoder.com

Debug Tools

How to override feature flags in the Flutter debug panel ​

Enterprise Reference for app_debugger_feature_flag: every variable, option and package it generates. Command: archipelago generate app_debugger_feature_flag

Generate the feature flag spoofer for app_debugger, see how the override delegate takes priority, set overrides in the panel and in code, and suspend one.

What you'll learn ​

  • Generating the feature flag spoofer sub-brick for app_debugger
  • Understanding how the spoofer delegate takes priority
  • Overriding flags in the panel and in code
  • Suspending an override without deleting it

Prerequisites ​

Step 1: Generate the Brick ​

bash
archipelago generate app_debugger_feature_flag

The CLI reads the two variables, uiKitPackageName and prefix, from archipelago.yaml. The spoofer works with any feature flag provider at runtime.

Step 2: Understand the Generated Packages ​

text
features/app_debugger_feature_flag/
├── app_debugger_feature_flag_api/   # FeatureFlagSpooferSDK, FlagOverride and SpooferUiKit
├── app_debugger_feature_flag/       # Debug implementation and Feature Flags tab
│   └── lib/src/
│       ├── feature_flag_spoofer_sdk_impl.dart
│       ├── feature_flag_spoofer_delegate.dart
│       └── feature_flag_spoofer_page.dart
└── app_debugger_feature_flag_noop/  # Production-flavor stub

FlagOverride holds a flagKey, a value and an enabled flag. Set enabled to false to suspend an override without removing it.

Step 3: Check build_prepare ​

The brick adds the swap entry to build_prepare.yaml for you:

yaml
build_prepare:
  mappings:
    - debug: app_debugger_feature_flag
      release: app_debugger_feature_flag_noop

The generated Fastfile runs dart run monorepo_toolkit build-prepare --flavor <flavor> before every build. Internal flavors keep the spoofer, also in release mode. The production flavor gets the noop.

Step 4: Check the Wiring ​

The hook wires the delegate for you. In features/app_debugger/app_debugger/lib/src/di/debug_tools.dart you find two blocks:

dart
// archipelago:debug_panel_pages:start
GetIt.I<FeatureFlagSpooferSDK>();
// archipelago:debug_panel_pages:end

// archipelago:feature_flag_spoofer_delegate:start
final spooferDelegate = GetIt.I<FeatureFlagSpooferSDK>().delegate;
if (spooferDelegate != null) {
  FeatureFlagDelegateRegistry.instance.register(spooferDelegate);
}
// archipelago:feature_flag_spoofer_delegate:end

FeatureFlagManager reads the registry on every evaluation. The spoofer runs before the real providers. The noop returns a null delegate, so nothing registers in production.

Step 5: Verify in an Internal Flavor ​

Open the debug panel and select the Feature Flags tool. It lists every flag your providers know, grouped by source. Flip the switch on a bool flag to override it; flip it back to the provider value to remove the override. Tap any other flag to edit it on a full-screen page (JSON gets a validating editor with a Format button). The spoofer switch at the top turns every override off without deleting it. Overrides survive a restart.

To draw the tool with your own design system, register a SpooferUiKit; see Match your design system.

To set an override from code, for example in an integration test:

dart
getIt<FeatureFlagSpooferSDK>().setOverride(
  FlagOverride(
    flagKey: 'new_checkout_flow',
    value: FeatureFlagResult.enabled(),
  ),
);

Step 6: Suspend, Remove or Clear ​

dart
final spoofer = getIt<FeatureFlagSpooferSDK>();

// Suspend: keep the entry, use the real provider value
spoofer.setOverride(
  FlagOverride(
    flagKey: 'new_checkout_flow',
    value: FeatureFlagResult.enabled(),
    enabled: false,
  ),
);

spoofer.removeOverride('new_checkout_flow');
spoofer.clearAllOverrides();

// Keep every override but stop applying them
spoofer.setSpoofing(enabled: false);

Overrides and the spoofer switch are saved to the app support directory and restored on the next launch. clearAllOverrides() removes the saved copy too.

Delegate Priority ​

PositionProviderNotes
FirstSpoofer delegatePanel overrides, highest priority
AfterYour real providersRemote config, local defaults

Next Steps ​