Bloc Signals Generator
🇪🇸 Leer en español
VSCode extension that automatically generates the base files (Bloc, Event, State) for the bloc_signals_flutter package and offers Code Actions to wrap code with the package's reactive widgets.
Features
File generation with a single command:
<snake>_bloc.dart — the BlocSignal<Event, State> class with initialState: and super(...) ready.
<snake>_event.dart — sealed class for events.
<snake>_state.dart — sealed class with a default initial state (<Name>Initial).
Native Dart snippets that appear when you type the prefix or from the snippets menu:
bsbuilder → BlocSignalBuilder
bslistener → BlocSignalListener
bsconsumer → BlocSignalConsumer
bsselector → BlocSignalSelector
bsprovider → BlocSignalProvider
bsmultiprovider → MultiBlocSignalProvider
bsread / bswatch / bsselect → BuildContext extensions
Code Actions with Cmd+. / Ctrl+. (in installations where the shortcut isn't remapped) to wrap selected code with reactive widgets.
Automatic widget detection when no text is selected: place the cursor anywhere inside a widget (even between letters of Padding) and the wrap will expand to cover the whole widget.
Auto-adds return and ; when missing on the selected code.
Automatically converts PascalCase names to snake_case for files and folders.
Final structure inside the destination folder:
<chosen-folder>/bloc/
├── <snake>_bloc.dart
├── <snake>_event.dart
└── <snake>_state.dart
Requirements
Usage
Generate a new Bloc
- Open a folder with your Flutter project in VSCode.
- Right-click on any folder in the explorer → Bloc Signals: Generate Bloc (Event/State/Bloc).
You can also invoke it from the command palette (Cmd/Ctrl + Shift + P).
- Type the Bloc name in PascalCase (e.g.
Counter, UserProfile).
- The extension will create the three
.dart files ready for you to add your events and transitions.
You have two ways to insert bloc_signals_flutter widgets:
Option A — Snippets (recommended, always works):
- Open any
.dart file.
- Type the snippet prefix (
bsbuilder, bslistener, bsconsumer, bsselector, etc.).
- A popup appears with the options. Select one with
Tab or Enter.
- Tab through the
<B> and <S> placeholders (and <R> for Selector) to fill them in.
- If you had text selected, it gets wrapped inside the callback (
$TM_SELECTED_TEXT).
You can also open the snippets menu manually with Cmd+Shift+P → "Snippets: Insert Snippet", or from Cmd+. if your installation has it configured that way.
Option B — Code Actions (visible only if Cmd+. isn't remapped):
- Without selection: place the cursor anywhere inside a widget — the wrap will automatically expand to cover the whole widget.
- With selection: select the fragment you want to wrap.
- Press
Cmd+. (macOS) or Ctrl+. (Windows/Linux).
- Choose one of the four "Wrap with..." options.
Note: extensions like Awesome Flutter Snippets remap Cmd+. to open their own snippet menu. In that case, use option A.
Behavior of the wrap
The Code Action automatically:
- Detects the enclosing widget if no text is selected (works even when the cursor is between letters of
Padding).
- Adds
return before the wrapped code if missing.
- Adds
; after the wrapped code if missing.
- Positions the cursor at the
${1:B} tabstop so you can immediately type the bloc type.
Generation example
Input: UserProfile.
Generated files:
// bloc/user_profile_bloc.dart
import 'package:bloc_signals/bloc_signals.dart';
import 'user_profile_event.dart';
import 'user_profile_state.dart';
class UserProfileBloc extends BlocSignal<UserProfileEvent, UserProfileState> {
UserProfileBloc() : super(initialState: UserProfileInitial()) {
// on<UserProfileEvent>((event, emit) {
// // TODO: handle event
// });
}
}
Code Action example
You have:
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Card(
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
// ...
),
),
),
),
);
}
Place the cursor anywhere inside Padding(...) (e.g. between Padd and ing) and press Cmd+. → choose Wrap with BlocSignalBuilder. Result:
Widget build(BuildContext context) {
return BlocSignalBuilder<UserProfileBloc, UserProfileState>(
builder: (context, state) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
child: Card(
child: InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
// ...
),
),
),
),
);
},
);
}
The cursor lands on <UserProfileBloc>, ready for you to replace it with the real type (use Tab to jump between <B> and <S>).
| Code Action |
Generics |
Where the wrapped code goes |
Wrap with BlocSignalBuilder |
<B, S> |
inside builder: |
Wrap with BlocSignalListener |
<B, S> |
inside child: (UI widget, no return) |
Wrap with BlocSignalConsumer |
<B, S> |
inside builder: |
Wrap with BlocSignalSelector |
<B, S, R> |
inside builder: |
Development
npm install
npm run compile # compile once
npm run watch # recompile on save
Press F5 in VSCode to launch the Extension Development Host and test the extension.
Packaging
npx vsce package
License
MIT.