Number of Used Widgets (NOUW)
v1.3.0
short name: NOUW
effort: 10m
default threshold: 20
Number of used widgets is a quantitative metric that measures the total number of used widgets in every method that returns a widget (e.g. build).
Why Be Cautious
High number of used widgets can lead to:
- Reduced Readability: Large widget trees can be overwhelming and hard to follow.
- Error-Proneness: Changes in large widget trees are more error-prone.
- Maintainability Challenges: Large
buildmethods often signal that the widget is doing too much. Splitting it into several widgets will not only simplify thebuildmethod, but also distribute other relevant methods among classes.
How to Address High Number of Used Widgets?
Try extracting custom widgets that encapsulate some part of the UI. While doing so also pay attention to the extracted code. Usually, during that process you will find multiple places with the similar code that can be replaced with the newly extracted widget.
Config Example
analysis_options.yaml
dcm:
metrics:
number-of-used-widgets:
threshold: 20
To set multiple threshold or other common config options, refer to the Configuring Metrics page.
Example
info
To view what contributes to the metric value, generate an HTML report.
❌ Bad: High Number of Used Widgets
// NOUW: 20
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Example Page'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text('Welcome to the example page!'),
SizedBox(height: 10),
Row(
children: [
Icon(Icons.info),
Text('Info'),
],
),
SizedBox(height: 10),
Row(
children: [
Icon(Icons.warning),
Text('Warning'),
],
),
SizedBox(height: 10),
Row(
children: [
Icon(Icons.error),
Text('Error'),
],
),
ElevatedButton(
onPressed: () {},
child: Text('Click Me'),
),
],
),
),
);
}
✅ Good: Low Number of Used Widgets
// NOUW: 12
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Example Page'),
),
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Text('Welcome to the example page!'),
SizedBox(height: 10),
IconTextRow(icon: Icons.info, label: 'Info'),
IconTextRow(icon: Icons.warning, label: 'Warning'),
IconTextRow(icon: Icons.error, label: 'Error'),
ElevatedButton(
onPressed: () {},
child: Text('Click Me'),
),
],
),
),
);
}
class IconTextRow extends StatelessWidget {
final IconData icon;
final String label;
IconTextRow({required this.icon, required this.label});
// NOUW: 4
Widget build(BuildContext context) {
return Row(
children: [
Icon(icon),
SizedBox(width: 5),
Text(label),
],
);
}
}