Answer: NO
Correct Location: .devcontainer/devcontainer.json
❌ WRONG:
docker-compose.yml
services:
flutter-dev:
initializeCommand: ... # ← NO! This doesn't exist here
✅ CORRECT:
.devcontainer/devcontainer.json
{
"initializeCommand": { # ← YES! Goes here
"adb": "path/to/script.sh"
}
}
Why: Docker Compose doesn't have lifecycle hooks. Only devcontainer.json has initializeCommand, onCreateCommand, postStartCommand, and postAttachCommand.
Answer: Use a Template System
Three Methods:
DevBench/FlutterBench/templates/flutter-devcontainer-template/
├── .devcontainer/devcontainer.json
├── .vscode/tasks.json
├── docker-compose.yml
└── Dockerfile
Usage:
cd Dartwingers
flutter create mynewapp
cd mynewapp
cp -r ../../DevBench/FlutterBench/templates/flutter-devcontainer-template/.devcontainer .
cp -r ../../DevBench/FlutterBench/templates/flutter-devcontainer-template/.vscode .
# Edit names/paths as needed# DevBench/FlutterBench/scripts/new-flutter-project.sh
./new-flutter-project.sh mynewapp ../../Dartwingers
# Auto-creates and configures everything// User snippets: flutter-devcontainer
// Type "flutter-devcontainer" in JSON files → auto-complete!Result: All new projects get consistent configuration automatically.
Answer: In .devcontainer/devcontainer.json
Complete Lifecycle:
{
"name": "MyApp Flutter Dev",
// HOST (Windows) - Before container creation
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
},
// CONTAINER - Once on creation
"onCreateCommand": {
"dependencies": "flutter pub get",
"precache": "flutter precache --android"
},
// CONTAINER - Every start
"postStartCommand": {
"doctor": "flutter doctor",
"devices": "adb devices"
},
// CONTAINER - When VS Code attaches
"postAttachCommand": "echo '✅ Ready!' && adb devices"
}Not in docker-compose.yml: Docker Compose has no equivalent lifecycle hooks.
Manual tasks go in: .vscode/tasks.json (for developer-triggered actions)
Answer: YES - Using Relative Paths
projects/
└── infrastructure/ # ← FIXED LOCATION (never moves)
└── mobile/
└── android/
└── adb/
├── docker/
├── compose/
└── scripts/
Each project calculates path to infrastructure using relative ../ notation:
Number of "../" = (Project depth from 'projects/' directory)
Dartwingers Projects (2 levels deep):
projects/
└── Dartwingers/ # Level 1
└── ledgerlinc/ # Level 2
→ ../../infrastructure/
Dartwingers/
└── lablinc/ # Level 2
→ ../../infrastructure/
DavinciDesigner (2 levels deep):
projects/
└── DavinciDesigner/ # Level 1
└── flutter-app/ # Level 2
→ ../../infrastructure/
Deeply Nested (3+ levels deep):
projects/
└── SomeProject/ # Level 1
└── mobile/ # Level 2
└── flutter-app/ # Level 3
→ ../../../infrastructure/
2 Levels Deep (most common):
{
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}3 Levels Deep:
{
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}Tasks automatically adjust since they use ${workspaceFolder}:
{
"label": "🚀 Start ADB Infrastructure",
"command": "${workspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}Change path depth based on project location.
| Project Path | Depth | Path to Infrastructure |
|---|---|---|
Dartwingers/ledgerlinc/ |
2 | ../../infrastructure/ |
Dartwingers/lablinc/ |
2 | ../../infrastructure/ |
Dartwingers/dartwing/ |
2 | ../../infrastructure/ |
DavinciDesigner/flutter-app/ |
2 | ../../infrastructure/ |
DevBench/FlutterBench/ |
2 | ../../infrastructure/ |
From ledgerlinc:
cd projects/Dartwingers/ledgerlinc
ls -la ../../infrastructure/mobile/android/adb/scripts/
# Should list: start-adb-if-needed.sh, stop-adb.sh, check-adb.shFrom DavinciDesigner:
cd projects/DavinciDesigner/flutter-app
ls -la ../../infrastructure/mobile/android/adb/scripts/
# Should list: start-adb-if-needed.sh, stop-adb.sh, check-adb.shTest Script Path:
cd projects/Dartwingers/ledgerlinc
../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh
# Should execute successfully- Portable: Works on any machine, any OS
- Version Control: Same paths in repo for all developers
- No Configuration: No environment variables needed
- Relocatable: Can move entire
projects/folder
// BAD - Hardcoded absolute path
{
"initializeCommand": {
"adb": "C:/projects/infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}Issues:
- Breaks on different machines
- Breaks on Linux/Mac
- Breaks if
projects/moves - Not in version control friendly
// BAD - Requires env var setup
{
"initializeCommand": {
"adb": "$FLUTTER_INFRA/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}Issues:
- Every developer must set
FLUTTER_INFRA - Different on Windows/Linux/Mac
- Easy to forget
- Setup overhead
// GOOD - Works everywhere
{
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}Benefits:
- Just works™
- No setup
- Cross-platform
- Maintainable
# 1. Create infrastructure at fixed location
cd projects
mkdir -p infrastructure/mobile/android/adb/{docker,compose,scripts}
# 2. Verify structure
ls -la infrastructure/mobile/android/adb/
# Should show: docker/ compose/ scripts/
# 3. Make scripts executable
chmod +x infrastructure/mobile/android/adb/scripts/*.sh
# 4. Verify from any project
cd Dartwingers/ledgerlinc
ls ../../infrastructure/mobile/android/adb/scripts/
# Should list all scriptsFor each Flutter project:
# 1. Count depth from projects/
# Dartwingers/ledgerlinc → 2 levels → ../../
# 2. Set in devcontainer.json
{
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../infrastructure/..."
}
}
# 3. Set in tasks.json
{
"command": "${workspaceFolder}/../../infrastructure/..."
}
# 4. Verify
cd Dartwingers/ledgerlinc
ls -la ../../infrastructure/mobile/android/adb/scripts/
# Should workIn template files, use placeholder that gets adjusted:
// DevBench/FlutterBench/templates/.../devcontainer.json
{
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}When copying to new project, adjust ../../ if needed based on depth.
projects/
└── ProjectFolder/
└── app/
Path: ../../infrastructure/
Projects:
- Dartwingers/*
- DavinciDesigner/flutter-app
- DevBench/*
projects/
└── ProjectFolder/
└── mobile/
└── flutter-app/
Path: ../../../infrastructure/
Count Rule: One ../ per level to projects/, then add infrastructure/
If you want to avoid counting levels, use symlinks:
# In each project root
cd Dartwingers/ledgerlinc
ln -s ../../infrastructure .infrastructure
# Then use fixed path
"initializeCommand": {
"adb": "${localWorkspaceFolder}/.infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}Pros: Always same path
Cons: Symlinks can be problematic on Windows, requires Git configuration
Recommendation: Stick with relative paths (simpler, more reliable)
projects/
├── infrastructure/ # ← PINNED HERE (never moves)
│ └── mobile/
│ └── android/
│ └── adb/
│ ├── docker/
│ ├── compose/
│ └── scripts/
│
├── Dartwingers/
│ ├── ledgerlinc/
│ │ ├── .devcontainer/
│ │ │ └── devcontainer.json # ← initializeCommand: ../../infrastructure/...
│ │ └── .vscode/
│ │ └── tasks.json # ← tasks use: ../../infrastructure/...
│ │
│ ├── lablinc/
│ │ ├── .devcontainer/
│ │ │ └── devcontainer.json # ← initializeCommand: ../../infrastructure/...
│ │ └── .vscode/
│ │ └── tasks.json # ← tasks use: ../../infrastructure/...
│ │
│ └── dartwing/
│ └── ...
│
└── DavinciDesigner/
└── flutter-app/
├── .devcontainer/
│ └── devcontainer.json # ← initializeCommand: ../../infrastructure/...
└── .vscode/
└── tasks.json # ← tasks use: ../../infrastructure/...
devcontainer.json:
{
"initializeCommand": {
"adb": "${localWorkspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}
}tasks.json:
{
"label": "🚀 Start ADB",
"command": "${workspaceFolder}/../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh"
}docker-compose.yml:
networks:
dartnet:
external: true
name: dartnet- Infrastructure at:
projects/infrastructure/mobile/android/adb/ - Scripts executable:
chmod +x *.sh - Path verified from each project:
ls ../../infrastructure/... - devcontainer.json has correct
../../count - tasks.json has correct
../../count - Template updated in FlutterBench/templates/
- Copy template files
- Adjust project name
- Adjust container name
- Verify path:
ls ../../infrastructure/mobile/android/adb/scripts/ - Open in VS Code
- Reopen in container
- Check initializeCommand output
- Run:
adb devices
- Open ledgerlinc (starts ADB)
- Open lablinc (reuses ADB)
- Both see same devices:
docker exec ledgerlinc-dev adb devices - Network connected:
docker network inspect dartnet
Cause: Wrong number of ../
Fix:
# From project directory, test path
cd Dartwingers/ledgerlinc
ls ../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh
# If not found, adjust ../ countCause: Scripts not executable
Fix:
chmod +x projects/infrastructure/mobile/android/adb/scripts/*.shCause: Windows path format
Fix: VS Code handles this automatically with ${localWorkspaceFolder}. If using Git Bash manually, ensure forward slashes:
# Good (forward slashes)
../../infrastructure/mobile/android/adb/scripts/start-adb-if-needed.sh
# Bad (backslashes)
..\..\infrastructure\mobile\android\adb\scripts\start-adb-if-needed.sh- initializeCommand location:
.devcontainer/devcontainer.json✓ - Easy task addition: Template system + copy script ✓
- Lifecycle tasks location:
.devcontainer/devcontainer.json✓ - Path pinning: Yes, using relative paths
../../infrastructure/✓
- Infrastructure: Fixed at
projects/infrastructure/ - Projects: Use relative paths (
../../,../../../, etc.) - Calculation: Count levels from
projects/, use that many../ - Verification: Test
ls ../../infrastructure/...from each project
- Create infrastructure once at
projects/infrastructure/ - Each project uses relative path in
devcontainer.json - Tasks automatically work with
${workspaceFolder}/../../ - Template ensures consistency for new projects
Result: Scalable, maintainable, portable configuration across all Flutter projects!