Skip to content
CARP Docs
Ask AISystemLightDark
On this page

Production builds

Exclude carp_debug_flutter from release and profile archives, and verify the exclusion.

Copy MarkdownOpen inOpen in GitHubView as MarkdownOpen in Scira AIOpen in ChatGPTOpen in ClaudeOpen in Cursor

Make sure the debug toolkit does not ship to end users. Declare carp_debug_flutter as a dev_dependency and reference it only from code gated behind kDebugMode. Flutter then strips dev-dependency plugins from release and profile builds, and Dart tree-shaking removes the unreachable debug code.

Exclude the toolkit

  1. Move it to dev_dependencies

    # pubspec.yaml
    dev_dependencies:
      carp_debug_flutter:
        git:
          url: https://github.com/carp-dk/carp_debug_flutter
  2. Keep production code free of the package

    No production-reachable file may import carp_debug_flutter. Read launch arguments through a small app-local hook that has no dependency on the package:

    // lib/core/debug/debug_hooks.dart  (no carp_debug_flutter import)
    String? Function(String key)? debugLaunchOverride;
    // anywhere config is read (BLoC, backend, ...)
    final mode = debugLaunchOverride?.call('deployment-mode')
        ?? const String.fromEnvironment('deployment-mode', defaultValue: 'production');

    In release builds the hook is null, so the --dart-define value is used and the package is never referenced.

  3. Isolate the toolkit wiring behind kDebugMode

    Put all package usage in one debug-only file, and call it only when kDebugMode is true:

    // lib/core/debug/debug_setup.dart  (imports carp_debug_flutter)
    // ignore_for_file: depend_on_referenced_packages
    import 'package:carp_debug_flutter/carp_debug_flutter.dart';
    // ...
    Future<void> initializeDebugTools() async {
      await DebugEnv().initialize();
      DebugEnv().registerAll(myEnvEntries);
      debugLaunchOverride = DebugEnv().overrideOf; // install the hook
    }
    Widget wrapWithDebugToolkit(Widget child, Database db) =>
        CarpDebugToolkit(config: ..., child: child);
    // lib/main.dart
    import 'core/debug/debug_setup.dart' as debug;
    
    Future<void> main() async {
      WidgetsFlutterBinding.ensureInitialized();
      if (kDebugMode) await debug.initializeDebugTools();
      final app = await buildApp();
      // Folds to `runApp(app)` in release; the wrapper is tree-shaken away.
      runApp(kDebugMode ? debug.wrapWithDebugToolkit(app, db) : app);
    }

Gate on kDebugMode, not kReleaseMode

Dev-dependency plugin natives are present only in debug builds. Gating on kDebugMode keeps the Dart usage aligned with native availability, so profile builds never hit a MissingPluginException.

What gets excluded per platform

Platform (release and profile builds)Dart codeNative plugin
AndroidTree-shaken outExcluded from the plugin registrant and the build
iOSTree-shaken outStill linked, but inert (see below)

iOS and macOS

Flutter does not yet filter dev-dependency plugins out of the native build on iOS and macOS (flutter#163874). The toolkit's small native stub is still linked into the archive, but it is inert: the Dart code that would call it is tree-shaken away, so it never runs and exposes no behavior.

Verify the exclusion

# Android: the release registrant must NOT mention the plugin.
flutter build apk --release
grep -i carp_debug_flutter \
  android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java
# -> no matches (in a debug build the same grep finds it)

# Confirm it is flagged as a dev dependency after `flutter pub get`:
grep -A2 carp_debug_flutter .flutter-plugins-dependencies   # "dev_dependency": true

Quick links

Page · DocumentationCARP DocumentationPage · DocumentationIntroductionPage · DocumentationQuickstartPage · DocumentationTerminologyPage · DocumentationArchitecture