Neovim plugin that accelerates Flutter Bloc/Cubit workflows. Generate files, update event handlers, wrap widgets, insert context accessors, and more -- all without leaving your editor.
- BlocCreate / CubitCreate -- Generate Bloc (3 files) or Cubit (2 files) scaffolds with
equatable,freezed, or plain templates, auto-detected frompubspec.yaml - BlocUpdateEvents -- Scan event hierarchy and add missing
on<Event>registrations + handler methods - Wrap with Bloc widgets -- Wrap any widget with
BlocBuilder,BlocSelector,BlocListener,BlocConsumer,BlocProvider, orRepositoryProvider - Unwrap -- Remove a Bloc widget wrapper and extract the child
- Convert to Multi* -- Convert nested
BlocProvider/BlocListener/RepositoryProviderinto theirMulti*equivalents - BlocRead / BlocWatch / BlocSelect -- Insert
context.read<T>(),context.watch<T>(), orcontext.select()with a type picker - Code Actions -- Context-aware action menu via
:BlocCodeAction(with optional none-ls/null-ls integration) - Project scanning -- Discovers all Bloc/Cubit types in your project for picker UIs
- Neovim >= 0.9
- A Flutter project with
flutter_bloc/blocin dependencies - plenary.nvim (for tests only)
No treesitter, LSP, or external binary dependencies required.
{
"pretodev/bloc.nvim",
ft = "dart",
opts = {},
}use {
"pretodev/bloc.nvim",
config = function()
require("bloc").setup()
end,
}All options with their defaults:
require("bloc").setup({
-- When both equatable and freezed are detected, prefer this style
style_priority = "equatable", -- "equatable" | "freezed"
-- Use Dart 3 sealed/final classes for events and states
use_sealed_classes = true,
-- Suffix for the initial state subclass
state_suffix = "Initial",
-- Enable none-ls/null-ls code action integration
none_ls = false,
-- Default keymaps (set individual keys to false to disable)
keymaps = {},
})| Command | Description |
|---|---|
:BlocCreate[!] Name path |
Generate Bloc files (bloc + event + state). Use ! to overwrite existing files. |
:CubitCreate[!] Name path |
Generate Cubit files (cubit + state). Use ! to overwrite. |
:BlocUpdateEvents |
Add missing event handler registrations in the current Bloc class. |
:BlocWrap |
Wrap widget at cursor with a Bloc widget (interactive picker). |
:BlocUnwrap |
Unwrap Bloc widget at cursor, extracting the child. |
:BlocConvert |
Convert nested Bloc widgets to Multi* variant. |
:BlocRead |
Insert context.read<T>() at cursor with type picker. |
:BlocWatch |
Insert context.watch<T>() at cursor with type picker. |
:BlocSelect |
Insert context.select() at cursor with type picker. |
:BlocCodeAction |
Show all available Bloc code actions at cursor. |
:BlocCreate Home lib/features/home/bloc
Creates three files in lib/features/home/bloc/:
home_bloc.darthome_event.darthome_state.dart
The template style (equatable/freezed/plain) is auto-detected from your pubspec.yaml.
Place your cursor inside a Bloc class body and run:
:BlocUpdateEvents
The plugin reads the event file, finds all concrete event subclasses, and adds missing on<Event> registrations and handler method stubs.
Place your cursor on a widget and run:
:BlocWrap
Select the wrapper type (e.g., BlocBuilder) and the Bloc/Cubit type from pickers. The widget is wrapped in-place.
When you have nested BlocProvider widgets:
BlocProvider<HomeBloc>(
create: (context) => HomeBloc(),
child: BlocProvider<AuthBloc>(
create: (context) => AuthBloc(),
child: AppView(),
),
)Place your cursor on the outer BlocProvider and run:
:BlocConvert
It becomes:
MultiBlocProvider(
providers: [
BlocProvider<HomeBloc>(
create: (context) => HomeBloc(),
),
BlocProvider<AuthBloc>(
create: (context) => AuthBloc(),
),
],
child: AppView(),
)Run :BlocCodeAction on any widget or Bloc class to see context-aware actions:
- Inside a Bloc class with missing event handlers: "Update Event Handlers (N missing)"
- On any widget: wrap/unwrap options
- On nested providers/listeners: convert to Multi* variant
Enable in setup to surface code actions through your LSP code action menu:
require("bloc").setup({
none_ls = true,
})Requires none-ls.nvim to be installed.
All functionality is accessible programmatically:
local bloc = require("bloc")
bloc.create_bloc("Home", "lib/features/home/bloc", { style = "equatable" })
bloc.create_cubit("Counter", "lib/counter", { style = "plain", force = true })
bloc.update_events()
bloc.wrap()
bloc.unwrap()
bloc.convert()
bloc.bloc_read()
bloc.bloc_watch()
bloc.bloc_select()
bloc.code_action()- No treesitter dependency -- Uses regex and bracket-counting parsing for reliable Dart code analysis
- Project scanning -- Reads
pubspec.yamlto detect dependencies and scanslib/for Bloc/Cubit classes - Style auto-detection -- When both equatable and freezed are present,
style_priorityconfig controls which template is used - Safe by default -- Won't overwrite existing files without
!bang, won't duplicate event registrations
Tests use plenary.nvim busted-style specs.
# Run all tests
make test
# Run a single test file
make test-file FILE=tests/bloc/parser_spec.luaMIT