Flutter Widget Architect — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Flutter Widget Architect (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Structure Flutter widget trees and state so rebuilds start and stop exactly where intended and build() is a pure, cheap function of state. The costly failure this prevents is the rebuild storm: one text field changes, the entire screen re-runs build(), the frame budget blows past 16ms (60fps) or 8ms (120fps), and the user sees jank that profiling later traces to architecture, not to any single slow line.
Order matters: decompose before optimizing subscriptions, because .select on a monolithic build() buys nothing.
Before restructuring a screen, establish:
debugPrintRebuildDirtyWidgets first and capture which widgets rebuild).Label any performance claim not backed by DevTools as a guess.
Split a deep build() into small StatelessWidget/StatefulWidget subclasses. Never extract subtrees into _buildX() helper methods - a helper always re-runs with its enclosing build(), while a separate const widget is skipped entirely when its inputs are unchanged. Mark const everywhere the analyzer allows; every const constructor is a subtree the framework never revisits.
Bad - the header re-runs every time the counter changes, because it is just a method on the same build:
class ProfileScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(children: [
_buildHeader(), // re-executes on every count change
Text('$count'),
]);
}
Widget _buildHeader() => Row(children: [Icon(Icons.person), Text('Profile')]);
}Good - the header is a const class; the framework short-circuits it, and only the Text depending on the counter rebuilds:
class ProfileScreen extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final count = ref.watch(counterProvider);
return Column(children: [
const _ProfileHeader(), // const: skipped when inputs unchanged
Text('$count'),
]);
}
}
class _ProfileHeader extends StatelessWidget {
const _ProfileHeader();
Widget build(BuildContext context) =>
const Row(children: [Icon(Icons.person), Text('Profile')]);
}build() must read state and return widgets - no network calls, timers, setState, or controller allocation. Create AnimationController, TextEditingController, StreamSubscription, and similar in initState or a provider, and dispose every one in dispose. A controller allocated in build() leaks one instance per rebuild.
autoDispose for screen-scoped data so leaving the screen releases it.StatefulWidget + setState for purely local ephemeral UI state (a toggle, one text field) - do not reach for a global store.Subscribe to one field with ref.watch(provider.select((s) => s.field)) or a Bloc buildWhen, never the whole model - watching the model rebuilds on every unrelated field change. Use ref.read for one-shot actions inside callbacks, never to react to changes.
Wrap only the changing subtree in Consumer/BlocBuilder/ValueListenableBuilder, and leave the static shell (AppBar, background, scaffold chrome) outside it. Pass child through builders to reuse subtrees the rebuild does not touch.
Use ListView.builder/SliverList so off-screen items are never built; a mapped Column or ListView(children: [...]) builds every item up front. Give items a ValueKey on the model id so element and state survive reorder.
Wrap flexible children in Expanded/Flexible inside Column/Row to prevent unbounded-height constraints, overflow errors, and layout thrash.
Produce a restructured screen containing: widget-class decomposition with const constructors, controllers created in initState/providers and disposed, one state tool scoped per Step 4, .select/buildWhen on every subscription, builders wrapping only changing subtrees, lazy keyed lists, and a before/after DevTools rebuild capture proving the static shell no longer rebuilds.
_buildX() helper methods that return Widget - they cannot be skipped, const classes can.ref.watch a whole model when you depend on one field; use .select/buildWhen.setState on a large State to update one value; isolate it with ValueListenableBuilder or a scoped provider.ListView/Column building every item or with unbounded height.debugPrintRebuildDirtyWidgets or DevTools rebuild tracking, not by inspection.AnimationController created is disposed; flutter analyze reports no dispose or prefer_const_constructors warnings.Column/ListView(children: [...]).StatefulWidget; shared/async/tested state lives in a provider or Bloc.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.