avoid_too_many_widgets_per_build
Flags a build method that creates more than max_widgets widgets (default 20).
This is the breadth counterpart to avoid_deep_widget_nesting. A tree can be shallow and still be too much for one method: thirty widgets in one build is a screen, a card, a header and a footer sharing a single scope, where nothing has a name and nothing can be reused or tested on its own.
Methods are matched on their return type, not their name — build, buildHeader and a Widget _row() helper are all measured, so an extracted helper does not escape the budget just by being called something else.
This rule is in the pedantic preset: a widget budget is a house style.
Enabling this rule
Section titled “Enabling this rule”many_lints: rules: avoid_too_many_widgets_per_build: enabled: true max_widgets: 20rules: avoid_too_many_widgets_per_build: enabled: true max_widgets: 20With max_widgets: 6, this build creates 9 and is reported. The diagnostic names both numbers, so you always know how far over you are:
@overrideWidget build(BuildContext context) => Column( children: [ Text('Settings'), Divider(), Row(children: [Icon(Icons.person), Text('Account')]), Row(children: [Icon(Icons.lock), Text('Privacy')]), ], );Give each group a name. The outer build is now 4 widgets, and each part can be const, reused, and tested on its own:
@overrideWidget build(BuildContext context) => const Column( children: [ Text('Settings'), Divider(), _AccountRow(), _PrivacyRow(), ], );
class _AccountRow extends StatelessWidget { const _AccountRow();
@override Widget build(BuildContext context) => Row(children: [Icon(Icons.person), Text('Account')]);}
class _PrivacyRow extends StatelessWidget { const _PrivacyRow();
@override Widget build(BuildContext context) => Row(children: [Icon(Icons.lock), Text('Privacy')]);}A builder closure has its own budget
Section titled “A builder closure has its own budget”Widgets built inside a builder: are counted separately, because that closure is its own build function. A list with a rich item template does not blow the page’s budget:
@overrideWidget build(BuildContext context) => ListView.builder( itemCount: 100, // These widgets count towards the closure, not towards build() itemBuilder: (context, index) => Card( child: Row( children: [Icon(Icons.star), Text('$index'), Icon(Icons.chevron_right)], ), ), );Renaming the method does not help
Section titled “Renaming the method does not help”The budget follows the return type, so splitting a long build into Widget _buildForm() in the same class buys nothing — each helper is measured on its own, and avoid_returning_widgets reports the helper besides. Extract a widget class instead.
Known limitations
Section titled “Known limitations”Only methods are measured. A top-level Widget page() => ... function is not.
Widgets built through a factory or returned by a helper you call are attributed to wherever the constructor literally appears, not to the call site.
Options
Section titled “Options”| Option | Type | Default | Description |
|---|---|---|---|
max_widgets |
int | 20 |
How many widgets one build method may create |
Turning this rule off
Section titled “Turning this rule off”To disable this rule:
rules: avoid_too_many_widgets_per_build: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_deep_widget_nesting— Keep a widget tree within a nesting budget.prefer_extracting_callbacks— Keep long callbacks out of the widget tree.avoid_returning_widgets— Extract widget helper methods into separate widget classes.avoid_single_child_in_multi_child_widgets— Don’t use Column, Row, or other multi-child widgets with only one child.