Skip to main content

graft_lint 1.0.0-alpha.4 copy "graft_lint: ^1.0.0-alpha.4" to clipboard
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 ๐ŸŒฑ #

pub package license Flutter 3.10+ custom_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 (or Ctrl + 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 to Error via analysis_options.yaml)
  • Rationale: Subclasses of GraftState that declare domain fields must override GraftProps 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 to Error via analysis_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)). Omitting schemaVersion, failing to provide zero-argument constructor defaults, or missing nested GraftModel factory 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.

1
likes
140
points
444
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Custom lint rules and analyzer plugin for the Graft reactive state management library.

Homepage
Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

analyzer, custom_lint_builder

More

Packages that depend on graft_lint