Quickstart
To make sure the toolkit is excluded from production archives you need to setup three parts: a launch-argument hook, a debug-only setup file, and a kDebugMode gate in main().
1. A launch-argument hook
Section titled “1. A launch-argument hook”Create a file with no dependency on carp_debug_flutter. Production
code reads launch arguments through this hook, so it never imports the package.
String? Function(String key)? debugLaunchOverride;The read the configuration anywhere in your app like the following: (in release the hook is null, so the compile-time value is used):
final mode = debugLaunchOverride?.call('deployment-mode') ?? const String.fromEnvironment('deployment-mode', defaultValue: 'production');2. A debug-only setup file
Section titled “2. A debug-only setup file”This is the only file that imports the package. The two tabs differ only in where the preferences and database come from.
// ignore_for_file: depend_on_referenced_packagesimport 'package:carp_debug_flutter/carp_debug_flutter.dart';import 'package:carp_mobile_sensing/carp_mobile_sensing.dart' show Settings;import 'package:flutter/widgets.dart';import 'package:sembast/sembast.dart';import 'debug_hooks.dart';
Future<void> initDebugTools() async { await DebugEnv().initialize(); DebugEnv().registerAll(myEnvEntries); debugLaunchOverride = DebugEnv().overrideOf; // install the hook}
Widget wrapWithToolkit(Widget child, Database db) => CarpDebugToolkit( config: DebugToolkitConfig( title: 'My CARP App Debug', envEntries: myEnvEntries, keyValueStores: [ SharedPreferencesKeyValueStore(Settings().preferences!, name: 'CARP Preferences'), ], databases: [SembastDebugDatabase(db, [ SembastStoreDescriptor('settings', StoreRef<String, Object?>.main()), SembastStoreDescriptor('results', intMapStoreFactory.store('result_store')), ])], onApply: () => bloc.backend.reconfigure(), // re-point CAWS live onReinitialize: () => bloc.backend.reconfigure(), // iOS restart path ), child: child,);// ignore_for_file: depend_on_referenced_packagesimport 'package:carp_debug_flutter/carp_debug_flutter.dart';import 'package:flutter/widgets.dart';import 'package:sembast/sembast.dart';import 'package:shared_preferences/shared_preferences.dart';import 'debug_hooks.dart';
Future<void> initDebugTools(SharedPreferences prefs) async { await DebugEnv().initialize(preferences: prefs); DebugEnv().registerAll(myEnvEntries); debugLaunchOverride = DebugEnv().overrideOf;}
Widget wrapWithToolkit(Widget child, SharedPreferences prefs, Database db) => CarpDebugToolkit( config: DebugToolkitConfig( title: 'My App Debug', envEntries: myEnvEntries, keyValueStores: [SharedPreferencesKeyValueStore(prefs)], databases: [SembastDebugDatabase(db, [ SembastStoreDescriptor('main', StoreRef<String, Object?>.main()), ])], onApply: () async => myAppConfig.reload(), ), child: child, );3. Gate it in main()
Section titled “3. Gate it in main()”main() imports only the app-local debug_setup.dart, and calls it solely when
kDebugMode. In release this folds to runApp(app) and the whole debug subtree
(and the package) is tree-shaken away.
import 'package:flutter/foundation.dart';import 'debug/debug_setup.dart' as debug;
Future<void> main() async { WidgetsFlutterBinding.ensureInitialized();
// Load overrides BEFORE the app reads its configuration. if (kDebugMode) await debug.initDebugTools();
final db = await openAppDatabase(); final app = await buildApp(db);
runApp(kDebugMode ? debug.wrapWithToolkit(app, db) : app);}Define your launch arguments
Section titled “Define your launch arguments”myEnvEntries is a list of EnvEntry
descriptors i.e. the values you normally pass via --dart-define:
const myEnvEntries = <EnvEntry>[ EnvEntry( key: 'deployment-mode', label: 'Deployment mode', group: 'Server', type: EnvValueType.enumeration, options: ['production', 'test', 'dev'], fallback: String.fromEnvironment('deployment-mode', defaultValue: 'production'), ), EnvEntry(key: 'server-host', label: 'App server host', group: 'Server'),];Opening it
Section titled “Opening it”You can either tap the bug icon in the app or just open and close it programmatically:
DebugController.instance.open();DebugController.instance.close();DebugController.instance.toggle();