Skip to content
| Marketplace
Sign in
Visual Studio Code>Programming Languages>Wrappy — Wrap with WidgetNew to Visual Studio Code? Get it now.
Wrappy — Wrap with Widget

Wrappy — Wrap with Widget

M. Yasin Senocak

| (0) | Free
Customizable "Wrap with widget" code actions for Flutter/Dart, powered by your own design system widgets.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Wrappy

Wrappy — Wrap with Widget

Customizable "Wrap with widget" code actions for Flutter/Dart.

Wrappy demo — wrapping a widget from the cmd+. menu


The Dart/Flutter extension's "Wrap with Column / Container / Center" quick fixes are baked into the analysis server and cannot be extended. Real projects collect their own widgets in a shared/ folder (your design system: AppButton, AppCard, AppScaffold …). Wrappy adds your own wrappers to the cmd+. (Quick Fix) menu:

// Cursor on Text → cmd+. → "Wrappy: AppButton" (under the Quick Fix group)
Text('Hello')

// Result:
AppButton(
  child: Text('Hello'),
  onPressed: ▮,        // ← cursor here (tabstop)
)

Features

  • 🧩 Wrap with your own widgets — every widget you define in config shows up in the cmd+. menu.
  • 🎯 Widget field or builder field — AppCard(child: …) or ResponsiveBuilder(builder: (context, constraints) => …).
  • ⌨️ Tabstops — fill fields like onTap: $1 right after wrapping (Tab to move between them).
  • 🧠 Smart detection — finds the bounds of the widget under the cursor with a fast, AST-free heuristic (aware of strings / comments / generics / named constructors); picks the innermost widget.
  • 🎨 Auto-format — triggers "Format Document" after wrapping so the code is properly indented.
  • 🛡️ Resilient — malformed config entries are skipped silently; the extension never crashes.

Requirements

  • VS Code ^1.85.0 (also works in OpenVSX-based editors like Cursor / Windsurf).
  • The Dart-Code extension is recommended — Wrappy works without it, but formatAfterWrap relies on a registered formatter, which the Dart extension provides.

Install

From the VS Code Marketplace — search for Wrappy in the Extensions view, or run:

ext install mysCod3r.wrappy

Or build and install a local .vsix:

npm install
npm run vsix          # produces wrappy-0.0.1.vsix
code --install-extension wrappy-0.0.1.vsix

Adding / managing wrappers

Three ways:

  • Side panel (Activity Bar → Wrappy) — recommended: manage all wrappers through a form. Every field is its own text input (labelled required/optional), a widget/builder selector, a live preview, and a scope (Workspace/User) selector. Edit/Delete from the list, Save from the form. Changes are written straight to settings.json.
  • Command wizard: Command Palette (Cmd/Ctrl+Shift+P) →
    • Wrappy: Add Wrapper — asks for the widget name, field type (widget/builder), field name, (for builder) the signature, and optional extra arguments (onTap: $1) step by step; writes to the Workspace or User settings.
    • Wrappy: Manage Wrappers — lists the defined wrappers and deletes the selected one.
  • Hand-edited JSON — write to settings.json with the schema below (for advanced / bulk edits).

titlePrefix, formatAfterWrap and ignoreWidgets can be edited directly in the Settings UI (Cmd+, → "wrappy").

Configuration

Add to settings.json (global) or .vscode/settings.json (workspace — overrides global):

{
  "wrappy.titlePrefix": "Wrappy: ",
  "wrappy.wrappers": [
    // --- widget field: "child" (the default) ---
    { "widget": "AppCard", "field": "child" },
    { "widget": "AppContainer", "field": "child" },

    // child field + an interactive tabstop you fill after wrapping
    { "widget": "AppButton", "field": "child", "snippetSuffix": "onPressed: $1" },

    // --- a different field name: "body" ---
    { "widget": "AppScaffold", "field": "body" },

    // --- builder field: standalone (context) ---
    { "widget": "AppBuilder", "field": "builder", "fieldType": "builder", "builderSignature": "(context)" },

    // --- builder field: (context, constraints) ---
    { "widget": "ResponsiveBuilder", "field": "builder", "fieldType": "builder", "builderSignature": "(context, constraints)" },

    // --- builder field: (context, index) ---
    { "widget": "AppListView", "field": "itemBuilder", "fieldType": "builder", "builderSignature": "(context, index)" },

    // --- named constructor — use a dot in the widget name ---
    { "widget": "AppButton.icon", "field": "child", "snippetSuffix": "onPressed: $1" },

    // --- raw template (full control) — must contain the $WIDGET$ placeholder ---
    { "widget": "AppPadding", "template": "AppPadding(\n  size: ${1:AppSpacing.md},\n  child: $WIDGET$,\n)" }
  ]
}

Named constructor: put a dot in the widget field: "widget": "AppButton.icon" → AppButton.icon(child: <widget>). The Add Wrapper wizard accepts this form too.

Editable variant (placeholder)

To make a value (e.g. a spacing token AppSpacing.md, or a named-constructor variant) a pre-selected, editable tabstop, use ${1:default} in a raw template:

{ "widget": "AppPadding", "template": "AppPadding(\n  size: ${1:AppSpacing.md},\n  child: $WIDGET$,\n)" }

After wrapping, AppSpacing.md comes up selected: press Tab/Esc to keep it, or type to change it. The same works for any field — $1, ${1:default}, $0 (final cursor).

Multi-line output: structured wrappers are produced multi-line automatically. If you use a raw template that contains a tabstop, format is skipped, so add \n yourself for a multi-line look (as above). insertSnippet aligns the lines to the cursor's indentation.

Wrapper fields

Field Required Default Description
widget ✓ unless template — Wrapper widget name, e.g. AppButton.
field — child Field the existing widget is placed in, e.g. child, body, title.
fieldType — widget widget or builder.
builderSignature — (context) Signature for fieldType: "builder", e.g. (context, constraints), (context, index).
snippetSuffix — — Arguments appended after the target field; may contain a tabstop, e.g. onTap: $1.
template — — Raw snippet template; overrides the other fields when present. Must contain $WIDGET$.
label — widget name Label shown in the menu.

Extension settings

Setting Default Description
wrappy.titlePrefix "Wrappy: " Prefix added before menu labels (e.g. Wrappy: AppButton).
wrappy.formatAfterWrap true Trigger "Format Document" after a wrap? (Skipped for tabstop wraps.)
wrappy.ignoreWidgets [] Wrap is not offered when the cursor is on one of these. Common non-widget types (EdgeInsets, TextStyle, Duration, Color, BoxDecoration …) are ignored by default; add your own types here.

How it works

  1. Put the cursor on the name of the widget you want to wrap (e.g. Text, Column). Wrappy only offers actions when the cursor is on a widget's name (head) — not between arguments, in whitespace, or after a comma.
  2. cmd+. (Quick Fix) / Ctrl+. on Windows/Linux.
  3. Each wrapper in wrappy.wrappers appears with a <titlePrefix><widget> label.
  4. Pick one → the existing widget is placed in the wrapper's field; the cursor lands on a tabstop if any.

Common non-widget constructors (EdgeInsets, TextStyle, Duration …) are ignored; add your own types with wrappy.ignoreWidgets.

Development

npm install
npm run watch          # esbuild watch
# F5 → Extension Development Host (opens the examples/ folder automatically)

npm run test:unit      # pure logic (span detection, templates) — fast
npm test               # integration (@vscode/test-electron)

See ROADMAP.md for the roadmap and decisions.

Limitations (v1)

  • Widget detection is heuristic (not an AST); it covers ~95% of cases. You need to put the cursor on the name of the widget you want to wrap. Without type information, non-widget constructors are filtered out with a built-in ignore list (+ wrappy.ignoreWidgets).
  • v1 only wraps. Unwrap, selection ranges and multi-cursor are on the roadmap (v2).

Contributing

Contributions are welcome! See CONTRIBUTING.md for setup, the dev workflow, tests and coding conventions. Good first issues: new builder signatures, more default ignoreWidgets, and the v2 items in ROADMAP.md (unwrap, multi-cursor).

License

MIT

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
© 2026 Microsoft