graft_lint 1.0.0-alpha.4
graft_lint: ^1.0.0-alpha.4 copied to clipboard
Custom lint rules and analyzer plugin for the Graft reactive state management library.
graft_lint ๐ฑ #
The official analyzer plugin and lint rules for Graft, the self-optimizing state management engine for Flutter.
graft_lint continuously analyzes your AST in real time to catch architectural anti-patterns, guarantee leak-free route lifecycles, and enforce 0-GC CPU bitmask diffing contracts with IDE red squiggles.
๐ Rules Included #
| Rule | Default Severity | Recommended Level | Description |
|---|---|---|---|
avoid_nested_graft_slot |
Error | ๐ด error |
Flags illegal nesting of slots (slot, slots, async, builder) within the same Graft instance. |
prefer_props_in_graft_state |
Info | ๐ด error |
Enforces overriding GraftProps get props => propsOf(...) in GraftState and GraftModel for CPU register diffing. |
require_graft_route_observer |
Info | ๐ด error |
Ensures GraftRouteTracker.observer is registered in MaterialApp to prevent controller memory leaks. |
require_dry_contracts |
Error | ๐ด error |
Enforces mandatory schemaVersion, constructor hydration defaults, and factory tear-offs for Frame-0 persistent states (Graft.dry). |
๐ Quick Setup #
1. Add Dependencies #
Add custom_lint and graft_lint as dev dependencies:
flutter pub add dev:custom_lint dev:graft_lint
Or manually in your pubspec.yaml:
dev_dependencies:
custom_lint: ^0.8.1
graft_lint: ^1.0.0-alpha.4
2. Configure analysis_options.yaml #
Activate the plugin in your root analysis_options.yaml. To show ๐ด red error squiggles in your IDE whenever rules are violated, promote their severity under analyzer.errors:
analyzer:
plugins:
- custom_lint
errors:
avoid_nested_graft_slot: error # ๐ด Flags illegal nested slots (default: error)
prefer_props_in_graft_state: error # ๐ด Promoted to error: flags missing propsOf(...)
require_graft_route_observer: error # ๐ด Promoted to error: flags missing route observer
require_dry_schema_version: error # ๐ด Mandatory: flags missing schemaVersion in dry states
Tip
๐ Why don't squiggles appear immediately in my IDE? #
The Dart Analysis Server caches plugin processes. After saving analysis_options.yaml:
- VS Code: Press
Cmd + Shift + P(orCtrl + Shift + P) and select Dart: Restart Analysis Server. - Android Studio: Open the bottom Dart Analysis tab and click the ๐ Restart Dart Analysis Server icon.
โ๏ธ Advanced Configuration #
Selectively Toggling Rules #
All rules are enabled by default. To disable or re-enable individual rules, configure the root custom_lint section in analysis_options.yaml:
custom_lint:
rules:
- avoid_nested_graft_slot: true
- prefer_props_in_graft_state: true
- require_graft_route_observer: false # Disable if not using route-scoped auto-disposal
๐ Rule Details & Examples #
1. avoid_nested_graft_slot #
- Default Severity:
Error(๐ด Red squiggly line) - Rationale: Graft slots (
graft.slot,graft(...),graft.slots,graft.async,graft.builder) are already isolated micro-diffing engines. Nesting a slot inside another slot of the same controller creates redundant reconciliation loops and subverts fine-grained rebuild insulation.
โ Bad
// โ Nesting a slot inside another slot of the same Graft instance
graft.slot(
builder: (s) => Column(
children: [
Text('Title: ${s.title}'),
graft((s) => Text('Name: ${s.name}')), // ๐ด avoid_nested_graft_slot error!
],
),
);
โ Good
// Option A: Use graft.slots for multi-child layouts
graft.slots(
layout: Column(crossAxisAlignment: CrossAxisAlignment.start, children: slots),
slots: (s) => [
Text('Title: ${s.title}'),
Text('Name: ${s.name}'),
],
);
// Option B: Use graft.compute for derived state
graft.compute<String>(
compute: (s) => '${s.title}: ${s.name}',
builder: (combined) => Text(combined),
);
2. prefer_props_in_graft_state #
- Default Severity:
Info(Promotable toErrorviaanalysis_options.yaml) - Rationale: Subclasses of
GraftStatethat declare domain fields must overrideGraftProps get props => propsOf(...). This contract allows Graft to compute dirty bitmasks directly in 64-bit CPU registers with 0 GC heap allocations during state mutations.
โ Bad
class UserState extends GraftState {
String name = '';
int score = 0;
// ๐ด prefer_props_in_graft_state: missing props getter!
}
โ Good
class UserState extends GraftState {
String name = '';
int score = 0;
@override
GraftProps get props => propsOf(name, score); // โก 64-bit CPU register diffing!
}
3. require_graft_route_observer #
- Default Severity:
Info(Promotable toErrorviaanalysis_options.yaml) - Rationale: Graft's zero-setup route injection (
context.use<T>()) relies on route navigation hooks to track screen lifecycles. Registering the route observer ensures route-scoped controllers cleanly self-dispose when their owning screen pops from the Navigator stack.
โ Bad
MaterialApp(
home: const HomeScreen(), // ๐ด require_graft_route_observer warning
);
โ Good
MaterialApp(
navigatorObservers: [GraftRouteTracker.observer], // or GraftRouteObserver()
home: const HomeScreen(),
);
4. require_dry_contracts #
- Default Severity:
Error(๐ด Red squiggly line) - Rationale: Frame-0 dry state persistence requires deterministic schema fingerprinting across VM reboots, and relies on clean constructor hydration (
super.dry(State.new)). OmittingschemaVersion, failing to provide zero-argument constructor defaults, or missing nestedGraftModelfactory tear-offs (factory => Model.new) leads to runtime state initialization crashes.
โ Bad
// โ Missing schemaVersion and required positional parameters without defaults
class SignUpState extends GraftState {
String username;
SignUpState(this.username); // ๐ด require_dry_default_constructor error!
// ๐ด require_dry_schema_version error!
}
โ Good
class SignUpState extends GraftState {
@override
int get schemaVersion => 1;
String username;
SignUpState([this.username = '']);
@override
String get dryKey => 'signup_draft';
@override
GraftProps get props => propsOf(username);
}
๐งช Terminal & CI/CD Verification #
Run custom_lint to verify all rules from your terminal or continuous integration pipeline:
dart run custom_lint
To fail CI pipelines with a non-zero exit code if any warning or error is detected:
dart run custom_lint --fatal-warnings
๐ License #
MIT License. Copyright (c) 2026 KM Muzahid. Free to use in commercial and open-source applications.