avoid_deep_widget_nesting
Flags a widget tree nested deeper than max_depth (default 8).
A build can be short and still be unreadable: eight levels of Padding inside Column inside Expanded push the widget that matters off the right edge, and every edit has to count brackets to find its place. Extracting a subtree into a named widget removes a level and names the part in one move.
Only widget instantiations are counted, so lists, closures and conditionals between them do not inflate the number. The diagnostic lands once per tree, on its root — the widget whose subtree has to be split — and the message says how far the subtree goes.
This rule is in the pedantic preset: a nesting budget is a house style.
See also: Flutter — performance best practices
Enabling this rule
Section titled “Enabling this rule”many_lints: rules: avoid_deep_widget_nesting: enabled: true max_depth: 8rules: avoid_deep_widget_nesting: enabled: true max_depth: 8Nine widget levels, one over the default budget of 8. The diagnostic is on Scaffold, the root:
@overrideWidget build(BuildContext context) => Scaffold( // 1 body: SafeArea( // 2 child: Padding( // 3 padding: const EdgeInsets.all(16), child: Column( // 4 children: [ Expanded( // 5 child: Center( // 6 child: Padding( // 7 padding: const EdgeInsets.all(8), child: Container( // 8 child: Text('Finally'), // 9 ), ), ), ), ], ), ), ), );Cut the tree where it has a name. Each half is now well inside the budget:
@overrideWidget build(BuildContext context) => Scaffold( body: SafeArea( child: Padding( padding: const EdgeInsets.all(16), child: const _Content(), ), ), );
class _Content extends StatelessWidget { const _Content();
@override Widget build(BuildContext context) => Column( children: [ Expanded(child: Center(child: Text('Finally'))), ], );}A builder starts a fresh tree
Section titled “A builder starts a fresh tree”A builder: closure is its own build function, so its depth is counted from zero rather than added to the caller’s. This tree is never over budget however long the page around it gets:
ListView.builder( itemCount: 10, itemBuilder: (context, index) => Padding( // depth 1 inside the closure padding: const EdgeInsets.all(8), child: Text('$index'), // depth 2 ),);Raising the budget
Section titled “Raising the budget”A screen-level widget in a design system can legitimately run deeper. Set the number once rather than annotating every file:
rules: avoid_deep_widget_nesting: max_depth: 12Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
max_depth |
int | 8 |
How many widget levels a tree may nest |
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: avoid_deep_widget_nesting: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_too_many_widgets_per_build— Keep one build method within a widget budget.prefer_extracting_callbacks— Keep long callbacks out of the widget tree.avoid_recursive_widget_calls— Don’t build a widget from inside its own build method.prefer_single_widget_per_file— Keep one public widget per file for better organization.