install.yaml Schema Reference
Canonical schema for the declarative install.yaml manifest consumed by plugin:install's manifest flow.
- Overview
- Top-Level Fields
- plugin_name
- pubspec / dev_pubspec / pubspec_assets (dependencies:)
- publish
- json_merge
- magic
- native
- env
- prompts
- placeholders
- post_install
- bootstrap_command
- Complete Example
Overview
install.yaml is the declarative manifest that drives plugin:install's manifest flow. The file is parsed by ManifestParser (lib/src/installer/manifest_parser.dart) into a typed InstallManifest (lib/src/installer/install_manifest.dart) and consumed by ManifestInstaller, which translates each section into deferred InstallOperations on the PluginInstaller transaction. Nothing writes to disk until commit(dryRun:, force:) fires.
The schema is intentionally tolerant: every section except plugin_name is optional, every section factory returns a sensible empty default when absent, and partial manifests parse cleanly. Schema violations raise ManifestValidationException; raw YAML syntax errors raise FormatException.
Top-Level Fields
| Field | Type | Required | Default | Source |
|---|---|---|---|---|
plugin_name |
string | yes | (none) | manifest_parser.dart:112 |
dependencies |
map | no | empty | manifest_parser.dart:124 |
publish |
map | no | empty | manifest_parser.dart:127 |
json_merge |
map | no | empty | manifest_parser.dart:128 |
magic |
map | no | empty | manifest_parser.dart:129 |
native |
map | no | empty | manifest_parser.dart:132 |
env |
map | no | empty | manifest_parser.dart:135 |
prompts |
list | no | empty | manifest_parser.dart:136 |
placeholders |
map | no | empty | manifest_parser.dart:137 |
post_install |
map | no | empty | manifest_parser.dart:138 |
bootstrap_command |
string | no | null |
manifest_parser.dart:141 |
plugin_name
The plugin's pubspec package name. Used to scope .artisan/installed/ reversibility records, the plugins.json registry entry, and the generated lib/app/_plugins.g.dart import line.
Validation regex (manifest_parser.dart:31):
^[a-z_][a-z0-9_]*$
The value must start with a lowercase letter or underscore, followed by any number of lowercase letters, digits, or underscores. An empty string raises ManifestValidationException('plugin_name must not be empty.').
plugin_name: example_plugin
dependencies
Pubspec mutations. The dependencies: map (note the YAML key is plural, but the typed model is PubspecDeps) carries three sub-keys, all optional.
| Sub-key | Type | Translates to |
|---|---|---|
pubspec |
Map |
dependencies: entries in the consumer's pubspec.yaml |
dev_pubspec |
Map |
dev_dependencies: entries |
pubspec_assets |
List |
Paths appended under flutter.assets: |
Empty maps and missing keys are interchangeable; both produce an empty PubspecDeps.
dependencies:
pubspec:
intl: ^0.20.0
dev_pubspec:
mocktail: ^1.0.0
pubspec_assets:
- assets/example/
publish
Stub-name to target-path mapping. Each entry becomes a publishConfig operation: the named stub is read from the plugin's install/ directory, placeholder-substituted, and written to the target path inside the consumer project.
| Key shape | Value shape |
|---|---|
| Stub name (string) | Target path (string) |
Non-string values raise FormatException('publish entry ".
publish:
install/example_config.dart.stub: lib/config/example.dart
json_merge
Map of target JSON file path to merge spec. Each entry triggers a mergeJson operation: the source stub is parsed as JSON, then merged into the target file according to additive.
| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
source |
string | yes | (none) | Stub key whose content is parsed as JSON. Missing or non-string raises FormatException('json_merge entry missing required "source" string.'). |
additive |
bool | no | true |
When true, existing keys in the target file survive a collision. When false, source keys overwrite target keys. |
json_merge:
assets/lang/en.json:
source: install/lang/en.json
additive: true
magic
Magic-framework integration block. Every slot is optional; the section defaults to an empty integration when absent.
| Field | Type | Translates to |
|---|---|---|
provider |
string (PascalCase) | injectProvider(provider) |
config_factory |
string | injectConfigFactory(name) |
routes |
string | injectRoute(name) |
Validation regex for provider (manifest_parser.dart:34):
^[A-Z][A-Za-z0-9]*$
The value, when present, must be a PascalCase identifier (uppercase first letter, alphanumerics afterwards). A non-matching value raises ManifestValidationException('magic.provider ".
magic:
provider: ExampleServiceProvider
config_factory: exampleConfig
routes: registerExampleRoutes
native
Per-platform native configuration. Each platform sub-section is independently optional.
native.android
| Field | Type | Translates to |
|---|---|---|
permissions |
List |
entries in AndroidManifest.xml |
meta_data |
Map |
entries in AndroidManifest.xml |
gradle.plugins |
List<{id, version?}> |
Gradle plugin block entries |
gradle.dependencies |
List<{scope, notation}> |
Gradle dependency block entries |
Gradle plugin entries without id raise FormatException('Gradle plugin missing required "id".'). Gradle dependency entries without both scope and notation raise FormatException('Gradle dependency entry requires both "scope" and "notation".').
native:
android:
permissions:
- android.permission.INTERNET
meta_data:
io.flutter.embedded_views_preview: "true"
gradle:
plugins:
- id: com.example.gradle.plugin
version: "1.0.0"
dependencies:
- scope: implementation
notation: "com.example:lib:1.0.0"
native.ios
| Field | Type | Notes |
|---|---|---|
info_plist |
Map |
Values may be String, bool, num, or List. Runtime-type branching happens in the dispatcher. |
entitlements |
Map |
Same shape as info_plist. |
podfile.platform_version |
string (optional) | Informational in v1: no chain method consumes it yet. |
podfile.pods |
List |
Pod declarations appended to target 'Runner'. |
native:
ios:
info_plist:
NSExampleUsageDescription: "Reason shown in iOS permission dialog"
UIBackgroundModes: ["fetch"]
entitlements:
com.apple.security.keychain: true
podfile:
platform_version: "13.0"
pods:
- "ExamplePod"
native.macos
Shape mirrors native.ios exactly. Same field set (info_plist, entitlements, podfile.platform_version, podfile.pods), same dispatcher rules. See the Complete Example for a macOS block in context.
native.web
| Field | Type | Translates to |
|---|---|---|
head_scripts |
List |
HTML snippets injected before in web/index.html |
meta_tags |
List |
attribute maps appended to web/index.html |
native:
web:
head_scripts:
- ''
meta_tags:
- {name: "description", content: "Example plugin metadata"}
env
Environment variables to inject into the consumer's .env. Map of var name to spec.
| Field | Type | Required | Notes |
|---|---|---|---|
default |
scalar | yes | Default value written into .env. Coerced via toString(). Missing raises FormatException('env entry missing required "default".'). |
comment |
string | no | Advisory documentation. The v1 dispatcher does NOT emit the comment into the .env file. |
Non-map entries raise FormatException('env entry ".
env:
EXAMPLE_KEY:
default: example_value
comment: "Example plugin runtime config"
prompts
Ordered list of prompts driven immediately at the start of install (before any deferred operation runs). Prompt answers populate the prompt-result map that placeholders interpolates.
| Field | Type | Required | Notes |
|---|---|---|---|
key |
string | yes | Unique within the prompts list. Duplicates raise ManifestValidationException('Duplicate prompt key ". |
type |
string | yes | One of string, bool, choice. |
question |
string | yes | Human-readable question text. |
default |
scalar | no | Stored as a String regardless of type for uniform placeholder substitution. |
options |
List |
no | For choice prompts: the valid option set. Empty for non-choice types. |
Missing key, type, or question raises FormatException('Prompt entry requires "key", "type", and "question" strings.').
prompts:
- {key: configPath, type: string, default: "~/.example.conf", question: "Config file path?"}
- {key: mode, type: choice, options: [dev, staging, prod], default: dev, question: "Deployment mode?"}
- {key: enableFeature, type: bool, default: false, question: "Enable optional feature?"}
placeholders
Map of placeholder key to value template. Values may reference prompt answers via {{ prompts.KEY }}. Whitespace around the dotted reference is optional, so {{prompts.x}} and {{ prompts.x }} are both accepted.
Reference regex (manifest_parser.dart:39):
\{\{\s*prompts\.([A-Za-z_][A-Za-z0-9_]*)\s*\}\}
Every {{ prompts.X }} reference must resolve to a declared prompt key. Unknown references raise ManifestValidationException('Placeholder ".
placeholders:
configFilePath: "{{ prompts.configPath }}"
runtimeMode: "{{ prompts.mode }}"
post_install
Post-install shell operations plus a final info message. Every sub-field is optional.
| Field | Type | Notes |
|---|---|---|
run |
List<{cmd, args?}> |
Shell commands run unconditionally at commit phase. cmd is required; missing raises FormatException('Shell spec missing required "cmd".'). |
ask_to_run |
List<{prompt, cmd, args?}> |
Shell commands run only after user confirmation. The prompt fires immediately at install time; on yes the shell call is deferred. Missing prompt or cmd raises FormatException('ask_to_run entry requires "prompt" and "cmd" strings.'). |
message |
string | Optional info message emitted after a Success result. |
post_install:
run:
- cmd: dart
args: [format, lib/config/example.dart]
ask_to_run:
- prompt: "Run pub get now?"
cmd: flutter
args: [pub, get]
message: |
example_plugin installed.
bootstrap_command
Optional plugin-specific bootstrap command name run after a Success result. Translates to a single dart run invocation deferred to the very end of the manifest flow.
bootstrap_command: example:install
Complete Example
The example below is the canonical _fullYaml test fixture from test/installer/manifest_parser_test.dart. Every section above appears here in context.
plugin_name: example_plugin
dependencies:
pubspec:
intl: ^0.20.0
crypto: ^3.0.0
dev_pubspec:
mocktail: ^1.0.0
pubspec_assets:
- assets/example/
- assets/lang/en.json
publish:
install/example_config.dart.stub: lib/config/example.dart
install/example_routes.dart.stub: lib/routes/example.dart
json_merge:
assets/lang/en.json:
source: install/lang/en.json
additive: true
magic:
provider: ExampleServiceProvider
config_factory: exampleConfig
routes: registerExampleRoutes
native:
android:
permissions:
- android.permission.INTERNET
meta_data:
io.flutter.embedded_views_preview: "true"
gradle:
plugins:
- id: com.example.gradle.plugin
version: "1.0.0"
dependencies:
- scope: implementation
notation: "com.example:lib:1.0.0"
ios:
info_plist:
NSExampleUsageDescription: "Reason shown in iOS permission dialog"
UIBackgroundModes: ["fetch"]
entitlements:
com.apple.security.keychain: true
podfile:
platform_version: "13.0"
pods:
- "ExamplePod"
macos:
info_plist:
NSExampleUsageDescription: "Reason shown in macOS permission dialog"
entitlements:
com.apple.security.network.client: true
podfile:
platform_version: "11.0"
pods:
- "ExamplePod"
web:
head_scripts:
- ''
meta_tags:
- {name: "description", content: "Example plugin metadata"}
env:
EXAMPLE_KEY:
default: example_value
comment: "Example plugin runtime config"
prompts:
- {key: configPath, type: string, default: "~/.example.conf", question: "Config file path?"}
- {key: mode, type: choice, options: [dev, staging, prod], default: dev, question: "Deployment mode?"}
- {key: enableFeature, type: bool, default: false, question: "Enable optional feature?"}
placeholders:
configFilePath: "{{ prompts.configPath }}"
runtimeMode: "{{ prompts.mode }}"
post_install:
run:
- cmd: dart
args: [format, lib/config/example.dart]
ask_to_run:
- prompt: "Run pub get now?"
cmd: flutter
args: [pub, get]
message: |
example_plugin installed.
bootstrap_command: example:install