Rules / Complexity / prefer-extracting-callbacks

prefer-extracting-callbacks

lint/complexity/prefer-extracting-callbacks
recommendedflutter
Same as prefer-extracting-callbacks · Dart Code Metrics

Flags an inline block-body callback passed to a widget constructor that should be extracted to a method.

Large inline closures in a build method bloat the widget tree, hurt readability, and can defeat const-ness; moving them to a named widget method clarifies intent and eases reuse. The rule only inspects classes that syntactically extend a Widget or State, and flags block-body function-expression arguments to widget constructions. Arrow callbacks, empty blocks, and Flutter builders (whose first parameter is typed BuildContext) are never flagged.

Options

allowed_line_count (integer, default: none) — when set, only callbacks spanning more than this many lines are flagged; unset flags every qualifying callback.

ignored_named_arguments (list of strings, default: empty) — named-argument labels whose callbacks are ignored.

Invalid

example.dartdart
import 'package:flutter/material.dart';

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: () {
        final now = DateTime.now();
        doSomething(now);
      },
      child: const Text('go'),
    );
  }
}

class MyState extends State<MyWidget> {
  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() {});
        doSomething(null);
      },
      child: const Text('tap'),
    );
  }
}

void doSomething(Object? x) {}
Prefer extracting the callback to a separate widget method.
8 return ElevatedButton(
9 onPressed: () {
Prefer extracting the callback to a separate widget method.
21 return GestureDetector(
22 onTap: () {

Valid

example.dartdart
import 'package:flutter/material.dart';

// Non-widget class: callbacks are never flagged (dcl only visits Widget/State).
class NotAWidget {
  void run(List<int> items) {
    items.forEach((item) {
      final doubled = item * 2;
      print(doubled);
    });
  }
}

class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        // Arrow callback: not a block body, never flagged.
        ElevatedButton(
          onPressed: () => doSomething(),
          child: const Text('go'),
        ),
        // Single-line block callback: within allowed_line_count, not flagged.
        ElevatedButton(
          onPressed: () { doSomething(); },
          child: const Text('short'),
        ),
        // Builder callback (first parameter is BuildContext) is excluded.
        Builder(
          builder: (BuildContext context) {
            final theme = Theme.of(context);
            return Text('$theme');
          },
        ),
      ],
    );
  }
}

void doSomething() {}

How to configure

Set the severity of prefer-extracting-callbacks in your falcon.json:

falcon.jsonjson
{
  "linter": {
    "rules": {
      "complexity": {
        "prefer-extracting-callbacks": "error"
      }
    }
  }
}