Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,34 @@ This package extends your Flutter development experience by providing convenient

Simply select the widget you want to wrap, and choose the appropriate "Wrap with..." command from the command palette, or use the provided snippets to quickly insert the desired wrapper code into your widget tree.

- **Wrap with SizedBox**: Surround your widget with a `SizedBox` to provide constraints on its size, such as width, height, or aspect ratio.
This extension includes the following standard "Wrap with..." commands:

- **Wrap with ListenableBuilder**: Easily wrap any widget with a `ListenableBuilder` to rebuild the widget based on changes in a `Listenable` object.
- **Wrap with ValueListenableBuilder<T>**: Automatically wrap your widget with a `ValueListenableBuilder` to react to changes in a `ValueListenable<T>`.
- **Wrap with RepaintBoundary**: Encapsulate your widget within a `RepaintBoundary` to isolate its repaint process, improving performance in complex UIs.
- **Wrap with SliverPadding**: Wrap your widget with a `SliverPadding` to add padding around a sliver widget in a `CustomScrollView`.

In order to add custom "Wrap with" commands, you can change the configuration in `flutter-plus.wraps` settings. The configuration is an array of objects with the following properties:

- **name**: The name of the command that will appear in the command palette.
- **body**: The snippet body that will be inserted into the editor when the command is executed. Use `${0...N}` to indicate the position of the selected text. In order to insert the selected text, use `${widget}`.

Example configuration:

```json
{
"wraps": [
{
"name": "Wrap with CustomWidget",
"body": [
"CustomWidget(",
" child: ${widget},",
")"
]
}
]
}
```

## Markdown snippets

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 63 additions & 21 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,26 +65,6 @@
"command": "flutter-plus.sealed-states",
"title": "Create Sealed States",
"category": "Flutter Plus"
},
{
"command": "flutter-plus.wrap-sizedbox",
"title": "Wrap with SizedBox",
"category": "Flutter Plus"
},
{
"command": "flutter-plus.wrap-listenablebuilder",
"title": "Wrap with ListenableBuilder",
"category": "Flutter Plus"
},
{
"command": "flutter-plus.wrap-valuelistenablebuilder",
"title": "Wrap with ValueListenableBuilder<T>",
"category": "Flutter Plus"
},
{
"command": "flutter-plus.wrap-repaintboundary",
"title": "Wrap with RepaintBoundary",
"category": "Flutter Plus"
}
],
"views": {
Expand Down Expand Up @@ -132,7 +112,69 @@
"language": "dart",
"path": "./snippets/flutter.json"
}
]
],
"configuration": {
"title": "Wraps",
"properties": {
"flutter-plus.wraps": {
"description": "Set up custom Wrap With here",
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Name of the Widget"
},
"body": {
"type": "array",
"items": {
"type": "string"
},
"description": "Body of the Widget, use $ for cursor position and ${widget} for selected text"
}
}
},
"default": [
{
"name": "ListenableBuilder",
"body": [
"ListenableBuilder(",
" listenable: ${1:listenable},",
" builder: (context, _) => ${widget},",
")"
]
},
{
"name": "ValueListenableBuilder",
"body": [
"ValueListenableBuilder(",
" valueListenable: ${1:valueListenable},",
" builder: (context, value, _) => ${widget},",
")"
]
},
{
"name": "RepaintBoundary",
"body": [
"RepaintBoundary(",
" child: ${widget},",
")"
]
},
{
"name": "SliverPadding",
"body": [
"SliverPadding(",
" padding: const EdgeInsets.all(${1:8.0}),",
" sliver: ${widget},",
")"
]
}
]
}
}
}
},
"extensionDependencies": [
"Dart-Code.dart-code",
Expand Down
29 changes: 9 additions & 20 deletions src/code-actions/code-action-wrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,26 @@

import { CodeAction, CodeActionKind, CodeActionProvider, window } from "vscode";
import { getSelectedText } from "../utils";
import { CodeWrap } from "../extension";

export class CodeActionWrap implements CodeActionProvider {
private wraps: Array<CodeWrap>;

constructor(customWraps: Array<CodeWrap>) {
this.wraps = customWraps;
}

public provideCodeActions(): CodeAction[] {
const editor = window.activeTextEditor;
if (!editor) return [];

const selectedText = editor.document.getText(getSelectedText(editor));
if (selectedText === "") return [];

return [
{
command: "flutter-plus.wrap-sizedbox",
title: "Wrap with SizedBox",
},
{
command: "flutter-plus.wrap-listenablebuilder",
title: "Wrap with ListenableBuilder",
},
{
command: "flutter-plus.wrap-valuelistenablebuilder",
title: "Wrap with ValueListenableBuilder<T>",
},
/* TODO: Convert between ListenableBuilder <--> ValueListenableBuilder */
{
command: "flutter-plus.wrap-repaintboundary",
title: "Wrap with RepaintBoundary",
}
].map((c) => {
return this.wraps.map((c) => {
let action = new CodeAction(c.title, CodeActionKind.Refactor);
action.command = {
command: c.command,
command: c.commandId,
title: c.title,
};
return action;
Expand Down
2 changes: 0 additions & 2 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
export * from "./sealed-states.command";

export * from "./wrap-with.command";
41 changes: 0 additions & 41 deletions src/commands/wrap-with.command.ts

This file was deleted.

24 changes: 24 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { workspace } from "vscode";

export type CustomWrapConfig = {
name: string,
body: Array<string>,
};

export class FlutterPlusConfig {
private static instance: FlutterPlusConfig;

public static getInstance(): FlutterPlusConfig {
if (!FlutterPlusConfig.instance) {
FlutterPlusConfig.instance = new FlutterPlusConfig();
}
return FlutterPlusConfig.instance;
}

public getCustomWraps(): Array<CustomWrapConfig> {
const config = workspace.getConfiguration('flutter-plus');
const customWraps = config.get<Array<CustomWrapConfig>>('wraps');

return customWraps || [];
}
}
71 changes: 52 additions & 19 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import * as vscode from 'vscode';

import {
sealedStates,
wrapWithListenableBuilder,
wrapWithRepaintBoundary,
wrapWithSizedBox,
wrapWithValueListenableBuilder,
} from "./commands";
Disposable
} from "vscode";

import {
CodeActionWrap,
} from './code-actions';
sealedStates
} from "./commands";

import { FlutterPlusConfig } from './config/config';
import { wrapWith } from './utils';


import {
Expand All @@ -21,6 +20,7 @@ import {
/* import fs from 'fs';
import path from 'path'; */

import { CodeActionWrap } from './code-actions';
import {
SdkCommands,
} from './utils';
Expand Down Expand Up @@ -63,9 +63,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
/// Register all commands.
function registerCommands(context: vscode.ExtensionContext) {
context.subscriptions.push(
/* vscode.commands.registerCommand('flutter-plus.helloWorld', () => {
vscode.window.showInformationMessage('Hello World from Flutter Plus!');
}), */
vscode.commands.registerCommand("flutter-plus.sealed-states", sealedStates),
);
}
Expand All @@ -88,16 +85,52 @@ function registerCommands(context: vscode.ExtensionContext) {

/// Register all wrappers (Wrap With...).
function registerWrappers(context: vscode.ExtensionContext) {
var wraps = $registerWrappers(context);
const disposable = vscode.workspace.onDidChangeConfiguration(event => {
if (!event.affectsConfiguration('flutter-plus')) {
return;
}

$unregisterWrappers(wraps);
wraps = $registerWrappers(context);
});

context.subscriptions.push(disposable);
}

function $unregisterWrappers(disposables: Array<Disposable>) {
disposables.forEach((disposable) => disposable.dispose());
}

function $registerWrappers(context: vscode.ExtensionContext): Array<Disposable> {
const configWraps = FlutterPlusConfig.getInstance().getCustomWraps();
const wraps: Array<CodeWrap> = configWraps.map((wrap) => {
return {
commandId: "flutter-plus.wrapWith." + wrap.name.toLowerCase().replace(/\s/g, "-"),
title: "Wrap with " + wrap.name,
command: () => wrapWith((selectedText) => wrap.body.join("\n").replace("\${widget}", selectedText)),
};
});

const subscriptions = [
...wraps.map((wrap) => {
return vscode.commands.registerCommand(wrap.commandId, wrap.command);
}),
vscode.languages.registerCodeActionsProvider(DART_MODE, new CodeActionWrap(wraps)),
];

context.subscriptions.push(
vscode.commands.registerCommand("flutter-plus.wrap-sizedbox", wrapWithSizedBox),
vscode.commands.registerCommand("flutter-plus.wrap-listenablebuilder", wrapWithListenableBuilder),
vscode.commands.registerCommand("flutter-plus.wrap-valuelistenablebuilder", wrapWithValueListenableBuilder),
vscode.commands.registerCommand("flutter-plus.wrap-repaintboundary", wrapWithRepaintBoundary),
vscode.languages.registerCodeActionsProvider(
DART_MODE,
new CodeActionWrap(),
),
...subscriptions,
);

return subscriptions;
}


export function deactivate() { }

export type CodeWrap = {
commandId: string,
title: string,
command: () => void,
};