Skip to content

avoid_too_many_widgets_per_build

v1.0.0WarningConfigurableWidget Best Practices

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.

analysis_options.yaml
many_lints:
rules:
avoid_too_many_widgets_per_build:
enabled: true
max_widgets: 20

With max_widgets: 6, this build creates 9 and is reported. The diagnostic names both numbers, so you always know how far over you are:

@override
Widget 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:

@override
Widget 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')]);
}

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:

@override
Widget 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)],
),
),
);

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.

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.

Option Type Default Description
max_widgets int 20 How many widgets one build method may create

To disable this rule:

many_lints.yaml
rules:
avoid_too_many_widgets_per_build: false

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