-
-
Notifications
You must be signed in to change notification settings - Fork 0
Output Formats
analyze and the instrumented app write JSON Lines: one self-contained JSON object per line. The
field names and types below come from the toJson methods in
lib/src/features/*/data/models/. Extracted Features explains how each static
metric is calculated.
One JSON object is written for each rebuild scope: a State subclass, a
ConsumerWidget/HookConsumerWidget, or the builder callback of a state-management widget
(BlocBuilder, Consumer, Obx, …). The analyze output contains exactly these
18 fields (AnalysisResultModel.toJson):
{
"instanceId": "42",
"filePath": "lib/screens/shopping_cart_screen.dart",
"scopeName": "_ShoppingCartScreenState",
"scopeType": "State",
"treeNonConstWidgetCount": 24,
"treeMaxWidgetNestingDepth": 7,
"treeListRenderingStrategy": 2,
"rootBuildReturnsConstWidget": 0,
"treeConstWidgetCount": 2,
"helperReferenceCount": 2,
"usesLayoutDependentBuilder": 1,
"treeCyclomaticComplexity": 5,
"treeIterationCount": 1,
"treeMaxIterationNestingDepth": 1,
"iterationWidgetCount": 3,
"valueObjectAllocCount": 4,
"helperWidgetCount": 3,
"helperMaxWidgetNestingDepth": 2
}| Field | Type | Description |
|---|---|---|
instanceId |
string | ID for this scope: hash of the root-relative path plus the scope name (and, for non-State scopes, its type and occurrence index). Used to join with runtime profiler data. It is not unique across transplanted targets that share one file/class, so the benchmark pipeline keys by sample_id instead. |
filePath |
string | Source path relative to the analyzed project root. |
scopeName |
string | Class name of the scope, or <Widget>_builder for a builder callback (e.g. BlocBuilder_builder). (Named stateClassName before rebuild-scope support.)
|
scopeType |
string | Kind of rebuild scope: State, ConsumerWidget, or the builder widget name (BlocBuilder, BlocSelector, BlocConsumer, Consumer, Selector, Obx, GetX, GetBuilder, Observer). |
treeNonConstWidgetCount |
int | Non-const widget instantiations across reachable build bodies; excludes const boundaries and helper-body widgets. |
treeMaxWidgetNestingDepth |
int | Maximum widget depth composed across reachable custom-widget build bodies. |
treeListRenderingStrategy |
0/1/2 | Worst list-rendering form across builds and helpers: 0 none, 1 lazy (.builder/.separated/.custom/.useDelegate, or a list that is lazy by contract), 2 eager O(N) (concrete children: on a scroll list, a SliverChildListDelegate, or runtime-length Column/Row/Wrap/Flex children). Scroll lists are ListView, GridView, ReorderableListView, PageView, ListWheelScrollView. (Replaced the buildUsesListViewBuilder boolean on 2026-07-20; widened beyond ListView/GridView/slivers on 2026-08-15.)
|
rootBuildReturnsConstWidget |
0/1 | Whether every top-level return of the root State.build is a const widget. (Any one const return set it before 2026-08-15.)
|
treeConstWidgetCount |
int | Const widget boundaries across reachable build bodies and helper bodies alike. (Named constConstructorRatio in JSONL produced before 2026-07-18; helper-body const widgets went to helperWidgetCount before 2026-08-15.)
|
helperReferenceCount |
int | Widget-returning helper reference sites in reachable build bodies: invocations, tear-offs, and explicit getters. Helpers returning List<Widget> count; SDK pass-throughs such as toList do not. |
usesLayoutDependentBuilder |
0/1 | Whether builds or helpers use LayoutBuilder, CustomMultiChildLayout, or Flow. |
treeCyclomaticComplexity |
int | Complexity summed across reachable build bodies, plus helper-body decision points without extra helper base points. |
treeIterationCount |
int | Loops, collection-for elements, linear collection operations, and List.generate across build and helper bodies. |
treeMaxIterationNestingDepth |
int | Maximum lexical iteration nesting within any analyzed build or helper body. |
iterationWidgetCount |
int | Non-const widgets built in per-element scopes across builds and helpers: loops, collection-op callbacks, List.generate, and lazy builders. Includes widgets built by a local function called from inside such a scope. |
valueObjectAllocCount |
int | All non-const, non-widget constructor allocations across build and helper bodies. |
helperWidgetCount |
int | Non-const widget instantiations in reachable helper bodies; const ones are counted by treeConstWidgetCount. (Included const widgets before 2026-08-15.)
|
helperMaxWidgetNestingDepth |
int | Maximum widget nesting within any individual reachable helper body. |
0/1fields are Dartbools serialized as1/0bytoJson. See Extracted Features for the precise definition of each metric.
Feature keys were renamed so every name states its scope: tree* = aggregated over the whole
static call tree, root* = root build() only, helper* = helper bodies only. Older JSONL
files can be normalized with this map:
| Old key (pre-2026-07-20) | Current key |
|---|---|
buildWidgetInstanceCount |
treeNonConstWidgetCount |
buildMaxWidgetNestingDepth |
treeMaxWidgetNestingDepth |
buildUsesListViewBuilder (bool, pre-07-20) / buildListRenderingStrategy
|
treeListRenderingStrategy |
buildReturnsConstWidget |
rootBuildReturnsConstWidget |
constConstructorRatio (pre-07-18) / constWidgetCount
|
treeConstWidgetCount |
buildHelperMethodCount |
helperReferenceCount |
buildCyclomaticComplexity |
treeCyclomaticComplexity |
buildIterationCount |
treeIterationCount |
buildMaxIterationNestingDepth |
treeMaxIterationNestingDepth |
helperMethodWidgetCount |
helperWidgetCount |
helperMethodMaxNestingDepth |
helperMaxWidgetNestingDepth |
usesLayoutDependentBuilder, iterationWidgetCount, valueObjectAllocCount, and the identity
keys (instanceId, filePath, stateClassName — since renamed to scopeName) are unchanged. The legacy boolean
buildUsesListViewBuilder maps onto the none/lazy levels of the ordinal only; it cannot
express eager (2).
analyze emits one row per rebuild scope, so a file can contribute several overlapping rows: a
State row counts the widgets built inside a nested BlocBuilder callback and the callback gets
its own row. That is deliberate — a parent rebuild re-runs the callback, while the callback row
measures the path its package can rebuild on its own.
Use --scope-types to narrow the output (--scope-types=State reproduces the pre-rebuild-scope
rows). inject only instruments State subclasses and skips rows of any other scopeType, so a
full-scope JSONL can be passed to it unchanged.
Two event types are emitted, distinguished by the event field. Which one you get depends on the
Flutter build mode (see inject): --profile → performance_metric,
--debug → dataflow-metric. Every event carries a timestamp (ISO-8601) and the instanceId
that joins back to the static-analysis record.
Performance event, emitted under --profile (PerformanceMetricsModel.toJson):
{
"timestamp": "2026-02-20T10:15:30.123Z",
"event": "performance_metric",
"instanceId": "42",
"buildSpan": 4123
}| Field | Type | Description |
|---|---|---|
timestamp |
string | ISO-8601 timestamp of the event. |
event |
string | Always "performance_metric". |
instanceId |
string | The instrumented State instance. |
buildSpan |
int | Build duration of the matched rebuild frame, in microseconds (0 on the 5-second timeout fallback). |
Dataflow event, emitted under --debug (DataflowMetricsModel.toJson):
{
"timestamp": "2026-02-20T10:15:30.456Z",
"event": "dataflow-metric",
"instanceId": "42",
"taintedRebuildCount": 5,
"totalWidgetCount": 42,
"maxNestingDepth": 7,
"taintedRatio": 0.119,
"opacityRebuildCount": 0,
"shaderMaskRebuildCount": 0,
"clipRRectRebuildCount": 1,
"clipOvalRebuildCount": 0,
"clipPathRebuildCount": 0,
"backdropFilterRebuildCount": 0
}| Field | Type | Description |
|---|---|---|
timestamp |
string | ISO-8601 timestamp of the event. |
event |
string | Always "dataflow-metric". |
instanceId |
string | The instrumented State instance. |
taintedRebuildCount |
int | Widgets that actually rebuilt as a result of the setState. |
totalWidgetCount |
int | Total widgets in the analyzed subtree. |
maxNestingDepth |
int | Maximum nesting depth of the analyzed subtree. |
taintedRatio |
double |
taintedRebuildCount / totalWidgetCount. |
opacityRebuildCount |
int | Rebuilt Opacity widgets. |
shaderMaskRebuildCount |
int | Rebuilt ShaderMask widgets. |
clipRRectRebuildCount |
int | Rebuilt ClipRRect widgets. |
clipOvalRebuildCount |
int | Rebuilt ClipOval widgets. |
clipPathRebuildCount |
int | Rebuilt ClipPath widgets. |
backdropFilterRebuildCount |
int | Rebuilt BackdropFilter widgets. |
Commands
Reference
Internals
Contributing