widget_analyser 0.1.0 copy "widget_analyser: ^0.1.0" to clipboard
widget_analyser: ^0.1.0 copied to clipboard

Offline Flutter widget analysis CLI for measuring widget complexity, nesting, usage, quality, and structural similarity. Produces console, JSON, and HTML reports, and integrates with CI quality gates.

widget_analyser #

pub package Dart SDK

widget_analyser is an offline Dart command line tool for reviewing Flutter widget code. It scans Dart files, discovers widget classes, measures build-method complexity, scores each widget, detects structural similarity, and writes human-readable or machine-readable reports.

The package is designed for local development, pull request checks, and CI quality gates where you want a quick answer to questions like:

  • Which widgets are becoming too large or too complex?
  • Which widgets have deep composition trees?
  • Which widgets have too many constructor parameters?
  • Which widgets instantiate many other widgets?
  • Which widgets look structurally similar and may be candidates for extraction?
  • Which widgets are referenced by other widgets in the same scanned project?

Contents #

Features #

  • Recursively scans .dart files under a selected folder.
  • Reads ignore patterns from configuration and from repeated --exclude CLI flags.
  • Uses the Dart analyzer AST instead of regular expressions.
  • Discovers classes that extend StatelessWidget, StatefulWidget, or State.
  • Calculates widget metrics from each discovered widget's build method.
  • Scores widgets with configurable metric thresholds and weights.
  • Classifies widgets into high, medium, and low quality grades.
  • Builds a widget usage graph for widgets found inside the scanned project.
  • Detects structurally similar build methods using token-based similarity.
  • Produces console, json, and html reports.
  • Loads configuration from analysis_options.yaml.
  • Can be used as a CLI or embedded as a Dart library.

Installation #

As a project dev dependency #

For most Flutter projects, add widget_analyser as a dev dependency:

dart pub add --dev widget_analyser

Then run it with dart run:

dart run widget_analyser lib

If your package manager resolves executables by package and executable name, this form is also valid:

dart run widget_analyser:widget_analyser lib

As a global tool #

You can also install the executable globally:

dart pub global activate widget_analyser

Then run it from any Flutter project:

widget_analyser lib

For global installs, make sure Pub's global bin directory is on your PATH. It is commonly:

~/.pub-cache/bin

See the Dart documentation for activating and running global packages.

Requirements #

  • Dart SDK >=3.3.0 <4.0.0
  • A Dart or Flutter project containing analyzable .dart files
  • Flutter widget classes that extend StatelessWidget, StatefulWidget, or State

The analyser runs locally. It does not upload source code or require a network connection to scan a project.

Quick start #

Analyse the lib directory of the current project:

dart run widget_analyser lib

Include structural similarity matches:

dart run widget_analyser lib --show-similarity

Write an interactive HTML report:

dart run widget_analyser lib --reporter html --output-dir build/reports

Write JSON for automation:

dart run widget_analyser lib --reporter json --output-dir build/reports

Print the resolved configuration:

dart run widget_analyser lib --print-config

Analyse the current directory by using the default --root-folder value:

dart run widget_analyser

CLI reference #

Usage: dart run widget_analyser [options] <directory>

The positional <directory> is the root folder to scan. If it is omitted, the CLI uses --root-folder, whose default value is ..

Option Short Default Description
--help -h false Prints usage information and exits.
--reporter <console|json|html> -r console Selects the output format.
--output-dir <path> -o not set Directory used when writing json or html files.
--show-similarity false Enables structural similarity detection and includes similar widget pairs in reports.
--threshold <value> config value Overrides the similarity threshold for the current run. Accepts either similarity form, such as 0.8, or distance form, such as 0.2.
--report-all false Includes high-quality widgets in report output. Without this flag, reports show only medium and low grade widgets.
--root-folder <path> . Folder used when no positional directory is provided.
--exclude <glob> none Adds an exclude glob. Repeat the flag to add multiple patterns. CLI excludes are appended to configured excludes.
--print-config -c false Prints the resolved configuration as JSON and exits without scanning.
--quiet -q false Suppresses all progress output. Recommended for CI and piped JSON workflows.

Exit codes #

Code Meaning
0 Analysis completed and no widget was graded low.
1 Analysis completed and at least one widget was graded low.
2 Invalid arguments, invalid threshold input, missing directory, or analysis failure.

The non-zero 1 result makes the tool suitable for CI quality gates. If you only want a report artifact and do not want low-quality widgets to fail the job, allow exit code 1 in your script.

Reports #

Console report #

The default console report is optimized for terminals and pull request logs:

dart run widget_analyser lib

It prints:

  • analysed root path
  • number of scanned Dart files
  • number of discovered widgets
  • number of low-quality widgets
  • one row per visible widget
  • grade, score, cyclomatic complexity, nesting level, parameter count, SLOC, build method lines, and reference count
  • up to three important violations per widget
  • optional similarity matches when --show-similarity is enabled

By default, high-quality widgets are hidden so the output stays focused on possible issues. Use --report-all to show everything:

dart run widget_analyser lib --report-all

JSON report #

Print JSON to stdout:

dart run widget_analyser lib --reporter json

Tip — piping JSON safely. All scan-progress messages go to stderr, so only the JSON payload reaches stdout. Add --quiet to suppress progress messages entirely when you pipe the output to another tool:

dart run widget_analyser lib --reporter json --quiet | jq '.widgets | length'

Write widget_analysis.json to a directory:

dart run widget_analyser lib --reporter json --output-dir build/reports

The JSON payload includes:

  • analysedAt
  • projectRoot
  • totalWidgets
  • lowQuality
  • mediumQuality
  • highQuality
  • resolved config
  • widgets
  • usageGraph

Each widget entry includes its name, file, kind, line range, grade, score, metrics, incoming references, outgoing widget uses, similarity matches, violations, deepest nesting path, and complexity breakdown.

Example shape:

{
  "analysedAt": "2026-04-30T06:30:00.000Z",
  "projectRoot": "/path/to/app/lib",
  "totalWidgets": 42,
  "lowQuality": 2,
  "mediumQuality": 8,
  "highQuality": 32,
  "widgets": [
    {
      "name": "ProfileHeader",
      "file": "features/profile/profile_header.dart",
      "kind": "stateless",
      "grade": "medium",
      "score": 0.68,
      "metrics": {
        "cyclomaticComplexity": 4,
        "nestingLevel": 6,
        "parameterCount": 7,
        "sloc": 58,
        "numberOfUsedWidgets": 12,
        "buildMethodLines": 74
      }
    }
  ]
}

HTML report #

Write an HTML report:

dart run widget_analyser lib --reporter html --output-dir build/reports

The command writes:

build/reports/widget_analysis.html

The HTML report contains embedded analysis data and client-side filtering. It shows summary cards, sortable metric columns, grade filters, clone filters, expandable widget details, similarity pairs, and the usage graph. If --output-dir is not provided, the HTML file is written to the scanned project root.

Configuration #

widget_analyser reads configuration from analysis_options.yaml in the scanned root directory.

Use a top-level widget_analyser section:

widget_analyser:
  metrics:
    cyclomatic-complexity: 10
    maximum-nesting-level: 5
    number-of-parameters: 6
    source-lines-of-code: 200
    build-method-lines: 250
    number-of-used-widgets: 20

  weights:
    cyclomatic-complexity: 0.25
    maximum-nesting-level: 0.20
    source-lines-of-code: 0.15
    build-method-lines: 0.15
    number-of-parameters: 0.15
    number-of-used-widgets: 0.10

  similarity-threshold: 0.80
  report-all: false
  exclude:
    - '**/*.g.dart'
    - '**/*.freezed.dart'
    - 'test/**'

The loader also checks a top-level dart_code_metrics section when widget_analyser is not present. Only keys understood by widget_analyser are applied.

Default configuration #

If no config file or no supported section exists, these defaults are used:

Key Default
cyclomatic-complexity 10
maximum-nesting-level 5
number-of-parameters 6
source-lines-of-code 200
build-method-lines 250
number-of-used-widgets 20
similarity-threshold 0.80
report-all false
exclude **/*.g.dart, **/*.freezed.dart, test/**

Config keys #

Key Type Description
metrics map of metric ID to integer Thresholds used for violations and score calculation.
weights map of metric ID to number Relative importance of each metric in the quality score. Values are normalized when their total is greater than zero.
similarity-threshold number or numeric string Minimum similarity required to report a pair. Values below 0.5 are treated as distance and converted to similarity.
report-all boolean Whether reports should include high-grade widgets by default.
exclude list of strings Glob patterns, relative to the scanned root, that should be skipped.

When exclude is set in YAML, it replaces the default exclude list. Add the generated-file defaults yourself if you still want them skipped.

CLI options can override parts of the resolved config:

  • --threshold overrides similarity-threshold for that run.
  • --report-all forces report-all to true.
  • --exclude appends extra patterns to the configured exclude list.

Unknown config keys are ignored with a warning.

Metric IDs and aliases #

Canonical IDs:

Metric ID Short alias Other accepted aliases
cyclomatic-complexity cc complexity
maximum-nesting-level nesting widgets-nesting-level, nest
number-of-parameters params
source-lines-of-code sloc loc
build-method-lines bml build-lines
number-of-used-widgets widgets

Metrics and scoring #

Each discovered widget receives raw metric values, violations, a weighted score, and a grade.

Discovered widgets #

The scanner discovers classes whose resolved supertypes include:

  • StatelessWidget
  • StatefulWidget
  • State

For each widget class, it records:

  • widget name
  • relative file path
  • start and end line
  • widget kind
  • constructor parameter count
  • build method AST
  • build method source
  • widgets instantiated inside the build method

Raw metrics #

Metric What it measures
Cyclomatic complexity Starts at 1 and adds points for branching constructs in build, including if, loops, switch cases, ternaries, logical operators, null-coalescing operators, catch, and assert.
Maximum nesting level Deepest nested widget instantiation chain inside build. The deepest path is retained for reports.
Number of parameters Parameter count for the primary constructor. The unnamed constructor is preferred when present.
Source lines of code Non-empty, non-comment lines in the extracted build body.
Build method lines Total number of lines in the extracted build body.
Number of used widgets Unique widget classes instantiated inside the build method.

Violations #

For each metric:

  • info is emitted when the value is at least 60% of the threshold.
  • warning is emitted when the value is above the threshold.
  • error is emitted when the value is more than 150% of the threshold.

Reports sort violations so more severe items appear first.

Quality score #

The score is a weighted value between 0.0 and 1.0.

For each metric, the analyser compares the raw value with the configured threshold. Values below the threshold score better. Values far above the threshold are clamped toward zero for that metric. The metric scores are multiplied by their configured weights and summed.

Grades are assigned from the final score:

Grade Score range
high >= 0.75
medium >= 0.50 and < 0.75
low < 0.50

The CLI exits with code 1 when any widget receives a low grade.

Similarity detection #

Similarity detection is disabled by default in the CLI because it compares widget pairs and can be more expensive on large projects.

Enable it with:

dart run widget_analyser lib --show-similarity

The detector tokenizes build method ASTs and compares token frequencies with Jaccard similarity. It keeps both raw and normalized token sequences:

  • raw tokens preserve constructor names and identifiers
  • normalized tokens focus on structural shape

Similarity matches are classified as:

Clone type Meaning
Type-1 Structural duplicate; literals may vary.
Type-2 Near duplicate; names may vary.
Type-3 Close clone with some additions or removals.
None Below clone classification thresholds.

The configured similarity-threshold controls the minimum raw similarity needed before a pair is reported.

Threshold values are normalized:

  • 0.80 means at least 80% similarity.
  • 0.20 is treated as a distance-style threshold and converted to 80% similarity.
  • values are clamped to the 0.0 to 1.0 range.

Using in CI #

Run the analyser after dependencies are installed:

dart pub get
dart run widget_analyser lib --show-similarity

Because exit code 1 means at least one low-quality widget was found, this command can fail a CI job intentionally:

name: widget-analyser

on:
  pull_request:

jobs:
  analyse:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dart-lang/setup-dart@v1
      - run: dart pub get
      - run: dart run widget_analyser lib --show-similarity --quiet

To produce a JSON report for downstream tooling without breaking on low-quality widgets:

set +e
dart run widget_analyser lib --reporter json --quiet > widget_analysis.json
status=$?
set -e

if [ "$status" -gt 1 ]; then
  exit "$status"
fi

To always upload an HTML report even when low-quality widgets exist:

set +e
dart run widget_analyser lib --reporter html --output-dir build/reports --show-similarity --quiet
status=$?
set -e

if [ "$status" -gt 1 ]; then
  exit "$status"
fi

Using as a library #

The package exports the analyser, config types, metric IDs, reporters, and report models:

import 'package:widget_analyser/widget_analyser.dart';

Future<void> main() async {
  final projectRoot = '/path/to/flutter_app';
  final config = ConfigLoader().load(projectRoot);

  final summary = await WidgetAnalyser(
    config: config,
    includeSimilarity: true,
  ).analyse(projectRoot);

  final json = JsonReporter().render(
    summary,
    reportAll: config.reportAll,
    showSimilarity: true,
  );

  print(json);
}

Public exports include:

  • WidgetAnalyser
  • AnalysisConfig
  • ConfigLoader
  • metric ID helpers
  • ConsoleReporter
  • JsonReporter
  • HtmlReporter
  • WidgetModel
  • WidgetReport
  • AnalysisSummary
  • MetricViolation
  • SimilarityMatch
  • related enums such as WidgetKind, QualityGrade, Severity, and CloneType

Practical examples #

Find large widgets but ignore generated files #

dart run widget_analyser lib \
  --exclude '**/*.g.dart' \
  --exclude '**/*.freezed.dart'

Generate a focused pull request report #

dart run widget_analyser lib --reporter console

This hides high-grade widgets by default and keeps attention on medium and low-grade widgets.

Generate a full architecture report #

dart run widget_analyser lib \
  --reporter html \
  --output-dir build/reports \
  --show-similarity \
  --report-all

Tune thresholds for a strict design system package #

widget_analyser:
  metrics:
    cyclomatic-complexity: 6
    maximum-nesting-level: 4
    number-of-parameters: 5
    source-lines-of-code: 80
    build-method-lines: 100
    number-of-used-widgets: 12
  weights:
    cyclomatic-complexity: 0.30
    maximum-nesting-level: 0.25
    number-of-parameters: 0.15
    source-lines-of-code: 0.15
    build-method-lines: 0.10
    number-of-used-widgets: 0.05

Limitations #

  • Similarity detection compares widget pairs, so very large projects can take longer when --show-similarity is enabled.
  • The usage graph only connects widgets discovered inside the scanned project. Flutter framework widgets and package dependencies may appear in per-widget uses data but are not graph nodes unless they are also discovered as project widgets.
  • Metrics are static heuristics. A low grade is a prompt for review, not proof that a widget is wrong.
  • The analyser focuses on widget build methods. Business logic outside build is not the primary scoring target.

Contributing #

Before sending changes, run:

dart pub get
dart format --set-exit-if-changed .
dart analyze
dart test

This repository uses package:lints/recommended.yaml with strict analyzer language settings.

An example/ directory at the repository root contains a ready-to-use analysis_options.yaml and a brief usage guide.

Changelog #

See CHANGELOG.md for the full release history.

License #

See the LICENSE file for details.

5
likes
130
points
180
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Offline Flutter widget analysis CLI for measuring widget complexity, nesting, usage, quality, and structural similarity. Produces console, JSON, and HTML reports, and integrates with CI quality gates.

Repository (GitHub)
View/report issues

Topics

#flutter #widget #static-analysis #cli

License

MIT (license)

Dependencies

analyzer, args, glob, path, yaml

More

Packages that depend on widget_analyser