Skip to content

avoid_returning_widgets

v0.4.0WarningConfigurableWidget Best Practices

This rule warns when a function, method, or getter returns a Widget. Building widgets inside helper methods is a common Flutter anti-pattern because the framework cannot optimize rebuilds for code that lives outside of a proper widget class.

When you extract UI into a _buildHeader() method instead of a _Header widget class, Flutter treats the entire parent widget as a single unit. It cannot skip rebuilding the header when only something else changed. Proper widget classes give Flutter the information it needs to do fine-grained rebuilds, which directly improves performance in complex UIs.

See also: Flutter performance best practices

Three shapes are exempt, because none of them collapses a subtree into the caller’s rebuild:

  • build() overrides — the standard way to build a widget.
  • A declaration passed as a callback rather than called, such as Builder(builder: _row). The framework invokes it at its own point in the tree, so it behaves like a widget class rather than an inlined helper. A declaration that is called to build inline is still reported.
  • Functions annotated for a functional-widget generator@FunctionalWidget, @swidget, @hwidget, @hcwidget — which generate a real widget class, so the rewrite this rule asks for has already happened. Configurable through ignored_annotations.

A getter is never treated as a callback: => _body reads it rather than tearing it off, so a bare reference to a Widget-returning getter is the inline build this rule targets.

class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(children: [_buildHeader(), _body]);
}
// Helper method returning a widget
Widget _buildHeader() => const Text('Header');
// Getter returning a widget
Widget get _body => const Text('Body');
}
class MyScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Column(children: [_Header(), _Body()]);
}
}
class _Header extends StatelessWidget {
const _Header();
@override
Widget build(BuildContext context) => const Text('Header');
}
class _Body extends StatelessWidget {
const _Body();
@override
Widget build(BuildContext context) => const Text('Body');
}

This rule is in the recommended preset, so it is on with preset: recommended (and with opinionated, which builds on it), or by name:

many_lints.yaml
rules:
avoid_returning_widgets: true

To turn it off again:

many_lints.yaml
rules:
avoid_returning_widgets: false

To keep the rule on but skip certain paths, use per-rule exclude.

analysis_options.yaml
many_lints:
rules:
avoid_returning_widgets:
ignored_names: [buildLeading]
additional_ignored_annotations: [widgetFactory]
allow_nullable: true
Option Type Default Description
ignored_names list of strings [] Method and function names never reported
ignored_annotations list of strings [FunctionalWidget, swidget, hwidget, hcwidget] Annotation names that exempt a member (written without @). Replaces the defaults; use additional_ignored_annotations to extend them
additional_ignored_annotations list of strings [] Annotation names to exempt in addition to the defaults
allow_nullable bool false Exempt nullable returns (Widget?), where null usually means “render nothing”