A feature-flag preprocessor for Flutter projects. Annotate your code with special directive comments and DartPP will comment out or restore entire blocks based on boolean flags in .dartpp.json — across Dart, YAML, Kotlin, Java, XML, and Gradle files.
Quick Start
1. Create .dartpp.json in your project root
{
"FIREBASE": true,
"ADS": false,
"SUPABASE": false
}
2. Annotate your files with directives
//#if FIREBASE
await Firebase.initializeApp();
//#endif
3. Run a command — Ctrl+Shift+P → DartPP: Apply Configuration
Commands
| Command | What it does |
|---|---|
DartPP: Apply Configuration |
Reads .dartpp.json, comments/uncomments all matching files, runs flutter pub get |
DartPP: Toggle Feature |
QuickPick list to flip a flag on/off — files update immediately |
DartPP: Reload Configuration |
Reload flags and refresh editor decorations (no file writes) |
DartPP: Wrap with Directive |
Wrap the selected lines with a directive block (right-click or palette) |
Directive Syntax
Dart / Kotlin / Java / Gradle
//#if FEATURE
//#ifndef FEATURE
//#elif OTHER
//#else
//#endif
YAML (pubspec.yaml)
#if FEATURE
#ifndef FEATURE
#elif OTHER
#else
#endif
XML (AndroidManifest.xml)
<!-- #if FEATURE -->
<!-- #ifndef FEATURE -->
<!-- #elif OTHER -->
<!-- #else -->
<!-- #endif -->
How Commenting Works
DartPP never removes code. Disabled blocks are commented out in-place and restored when re-enabled. The operation is fully idempotent — safe to run multiple times.
Dart / Kotlin / Java / Gradle — block comment /* */
Input (FIREBASE: false):
//#if FIREBASE
await Firebase.initializeApp();
FirebaseNotification.initForegroundNotificationListener();
//#endif
After Apply:
//#if FIREBASE
/*
await Firebase.initializeApp();
FirebaseNotification.initForegroundNotificationListener();
*/
//#endif
The /* and */ markers exist in the file (so the compiler ignores the block) but are visually hidden in VS Code — you only see the greyed-out code, not the comment markers.
YAML — line prefix #
#if FIREBASE
# firebase_core: ^4.1.0
# firebase_messaging: ^16.0.1
#endif
XML — block comment <!-- -->
<!-- #if FIREBASE -->
<!--
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
-->
<!-- #endif -->
Nested Directives
Full nesting is supported. Inner /* */ markers are only added when the outer scope is active, preventing invalid nested block comments.
//#if FIREBASE
await Firebase.initializeApp();
//#if ADS
FirebaseAdMob.instance.initialize();
//#endif
//#endif
Supported Directives
| Directive | Meaning |
|---|---|
#if FEATURE |
Active when FEATURE is true |
#ifndef FEATURE |
Active when FEATURE is false |
#elif FEATURE |
Else-if branch |
#else |
Fallback branch |
#endif |
Close the current block |
Full example:
//#if FIREBASE
print('Firebase');
//#elif SUPABASE
print('Supabase');
//#else
print('No backend');
//#endif
Editor Decorations
Open any supported file and DartPP automatically annotates it:
- Directive line — inline label:
✓ FIREBASE enabled(green) or✗ FIREBASE disabled(red) - Disabled block lines — dimmed/greyed out
/**//<!---->markers — rendered transparent (invisible) so the file looks clean
No configuration needed — decorations update live as you edit.
CodeLens
Above every #if / #ifndef block two clickable items appear:
$(pass-filled) FIREBASE — Enabled Disable
$(circle-slash) ADS — Disabled Enable
Clicking Enable or Disable toggles the flag, saves .dartpp.json, and immediately applies changes to all project files.
Wrap with Directive
Select any lines → right-click → DartPP: Wrap with Directive (or use the Command Palette).
Choose a pattern:
| Pattern | Result |
|---|---|
#if FEATURE |
Single guarded block |
#ifndef FEATURE |
Inverted guard |
#if … #else … #endif |
Two-branch |
#if … #elif … #else … #endif |
Three-branch |
The directive style (//, #, <!-- -->) is chosen automatically based on the file type.
Files Processed
| Included | Excluded |
|---|---|
lib/**/*.dart |
.dart_tool/** |
pubspec.yaml |
build/** |
android/**/*.kt |
.git/** |
android/**/*.java |
.gradle/** |
android/**/*.xml |
.idea/** |
android/**/*.gradle |
|
android/**/*.gradle.kts |
|
*.gradle (root) |
|
*.gradle.kts (root) |
Settings
| Setting | Default | Description |
|---|---|---|
dartpp.configFile |
.dartpp.json |
Config file path relative to workspace root |
dartpp.runFlutterPubGet |
true |
Run flutter pub get after Apply Configuration |
Output Channel
All activity is logged to View → Output → DartPP:
- Feature flags loaded (enabled / disabled counts)
- Files processed or skipped
flutter pub getstdout and stderr- Decoration parse results
Building & Installing
npm install
npm run compile # compile TypeScript → out/
npm run package # produce dartpp-1.0.0.vsix (requires @vscode/vsce)
Install the .vsix:
code --install-extension dartpp-1.0.0.vsix
Or: Extensions panel → ··· → Install from VSIX…
The extension activates automatically when the workspace contains .dartpp.json.
Project Structure
src/
├─ extension.ts Activation, commands, file watcher
├─ commands/
│ ├─ applyConfiguration.ts Apply flags to all files + flutter pub get
│ ├─ toggleFeature.ts QuickPick toggle + auto-apply
│ ├─ reloadConfiguration.ts Reload flags + refresh decorations
│ └─ wrapWithDirective.ts Wrap selection with directive block
├─ parser/
│ ├─ lexer.ts Tokenise directives (Dart / YAML / XML styles)
│ ├─ parser.ts Stack-based AST builder
│ ├─ evaluator.ts AST → output + block metadata
│ └─ processor.ts Public API: commentOutSource, validateDirectives
├─ config/
│ └─ configManager.ts Load / save / toggle .dartpp.json
├─ decorations/
│ └─ decorationProvider.ts Labels, dimming, hidden /* */ markers
├─ codelens/
│ └─ codeLensProvider.ts Clickable Enable / Disable above each block
└─ utils/
└─ fileUtils.ts File collection (Dart, YAML, Android, Gradle)