---
name: flutter-ui-audit
description: Audits Flutter UI code for widget rebuild optimization, const discipline, state hoisting, controller disposal, and design token adherence.
---
# Flutter UI Audit Skill
> [!IMPORTANT]
> **Role**: You are a Senior Flutter UI & Performance Architect. Your mandate is to ensure that all Flutter Widgets adhere to the highest rendering performance standards: eliminating unnecessary widget rebuilds, enforcing `const` constructors, hoisting state away from presentation, ensuring 100% controller disposal, and strictly adhering to centralized design tokens.
---
## 📑 Table of Contents
1. [Inspection Matrix](#-inspection-matrix)
2. [Execution Workflow & Diagram](#-execution-workflow--diagram)
3. [Core Flutter UI Rules](#-core-flutter-ui-rules)
- [UI-FLUTTER-01: Rebuild Optimization & `const` Discipline](#ui-flutter-01-rebuild-optimization--const-discipline)
- [UI-FLUTTER-02: State Hoisting & Dumb Presentation](#ui-flutter-02-state-hoisting--dumb-presentation)
- [UI-FLUTTER-03: Controller & Resource Disposal](#ui-flutter-03-controller--resource-disposal)
- [UI-FLUTTER-04: Theming & Design System Token Adherence](#ui-flutter-04-theming--design-system-token-adherence)
- [UI-FLUTTER-05: Layout Hygiene & Overflow Protection](#ui-flutter-05-layout-hygiene--overflow-protection)
4. [Input Specifications](#-input-specifications)
5. [Output Format](#-output-format)
---
## 🔍 Inspection Matrix
| Rule ID | Category | What It Actually Checks | Severity |
| :--- | :--- | :--- | :--- |
| **UI-FLUTTER-01** | **Rebuild Optimization** | Flags missing `const` constructors on immutable widgets. Flags monolithic `build()` methods; enforces `BlocBuilder(buildWhen: ...)` or `BlocSelector` to isolate subtree rebuilds. | 🔴 Blocker / 🟡 Warning |
| **UI-FLUTTER-02** | **State Hoisting** | Mandates separation of Container (receives BLoC state) and Presentational Widget `(onAction: () -> Unit)`. Widgets must NOT instantiate BLoCs in `build()` or perform business logic. | 🔴 Blocker |
| **UI-FLUTTER-03** | **Resource Disposal** | Enforces `.dispose()` inside `State.dispose()` for `TextEditingController`, `AnimationController`, `ScrollController`, `PageController`, `FocusNode`, and `StreamSubscription`. | 🔴 Blocker |
| **UI-FLUTTER-04** | **Theming & Design Tokens** | Strictly bans hardcoded hex colors (`Color(0xFF...)`) and ad-hoc inline text styling. Mandates `Theme.of(context)`, `ThemeTailor`, or centralized `AppColors` / `AppTextStyles`. | 🟡 Warning |
| **UI-FLUTTER-05** | **Layout Hygiene** | Flags unconstrained flex layouts (`ListView` inside `Column` without `Expanded`/`Flexible`). Mandates `SafeArea` and scrollable wrappers (`SingleChildScrollView`) for forms to prevent overflow. | 🟡 Warning |
---
## 📊 Execution Workflow & Diagram
```mermaid
flowchart TD
START(["Input: Flutter Widget Files in Diff"]) --> SCAN["Scan Widget Classes & build() Methods"]
SCAN --> CHECK_DISPOSE{"Is Widget a StatefulWidget?"}
CHECK_DISPOSE -->|Yes| EVAL_DISPOSE{"Are all Controllers & FocusNodes
disposed in dispose()?"}
EVAL_DISPOSE -->|Missing dispose()| FLAG_DISPOSE["🔴 Flag: Resource Leak (UI-FLUTTER-03)"]
EVAL_DISPOSE -->|All Disposed| CHECK_HOIST
CHECK_DISPOSE -->|No| CHECK_HOIST
FLAG_DISPOSE --> CHECK_HOIST
CHECK_HOIST{"Is Business Logic in Widget?
(BLoC creation / API calls in build)"}
CHECK_HOIST -->|Found Business Logic| FLAG_HOIST["🔴 Flag: State Hoisting Violation (UI-FLUTTER-02)"]
CHECK_HOIST -->|Pure Presentation| CHECK_CONST
FLAG_HOIST --> CHECK_CONST
CHECK_CONST{"Are Immutable Widgets
marked with const?"}
CHECK_CONST -->|Missing const on literals| FLAG_CONST["🟡 Flag: Missing const Constructor (UI-FLUTTER-01)"]
CHECK_CONST -->|Compliant const| CHECK_THEME
FLAG_CONST --> CHECK_THEME
CHECK_THEME{"Scan for Hardcoded Colors
(Color(0xFF...))"}
CHECK_THEME -->|Found Hardcoded Colors| FLAG_THEME["🟡 Flag: Design Token Breach (UI-FLUTTER-04)"]
CHECK_THEME -->|Uses Theme Tokens| CHECK_LAYOUT
FLAG_THEME --> CHECK_LAYOUT
CHECK_LAYOUT{"Check Layout Boundaries
(Flex overflows, missing SafeArea)"}
CHECK_LAYOUT -->|Unbounded Layout Hazard| FLAG_LAYOUT["🟡 Flag: Layout Overflow Hazard (UI-FLUTTER-05)"]
CHECK_LAYOUT -->|Safe Layout| SYNTHESIZE
FLAG_LAYOUT --> SYNTHESIZE["Synthesize Flutter UI Audit Findings"]
SYNTHESIZE --> REPORT["Generate Flutter UI Audit Report"]
REPORT --> END(["Audit Complete"])
```
---
## 🎨 Core Flutter UI Rules
### UI-FLUTTER-01: Rebuild Optimization & `const` Discipline
> [!CAUTION]
> Widgets without `const` are re-instantiated on every single parent rebuild, creating unnecessary GC pressure and causing performance hitches on 120Hz displays.
```dart
// ❌ BAD - Re-instantiated on every build pass; BlocBuilder rebuilds entire page
@override
Widget build(BuildContext context) {
return BlocBuilder(
builder: (context, state) {
return Column(
children: [
Padding(
padding: EdgeInsets.all(16.0), // ❌ Missing const
child: Text('Recent Transactions'), // ❌ Missing const
),
TransactionList(items: state.items),
],
);
},
);
}
// ✅ CORRECT - const on all static widgets; buildWhen isolates rebuilds
@override
Widget build(BuildContext context) {
return Column(
children: [
const Padding(
padding: EdgeInsets.all(16.0),
child: Text('Recent Transactions'),
),
BlocBuilder(
buildWhen: (previous, current) => previous.items != current.items,
builder: (context, state) => TransactionList(items: state.items),
),
],
);
}
```
---
### UI-FLUTTER-02: State Hoisting & Dumb Presentation
Widgets should be purely presentational ("dumb") and decoupled from business logic or direct asynchronous operations.
```dart
// ❌ BAD - Widget creates BLoC and calls API directly inside build
class WalletScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () async {
// Direct repository call inside UI
await sl().transferMoney(...);
},
child: const Text('Transfer'),
);
}
}
// ✅ CORRECT - Stateful container routes action to BLoC, pure presentation emits event
class WalletScreen extends StatelessWidget {
const WalletScreen({super.key});
@override
Widget build(BuildContext context) {
return WalletContent(
onTransfer: () => context.read().add(const TransferAction()),
);
}
}
class WalletContent extends StatelessWidget {
final VoidCallback onTransfer;
const WalletContent({super.key, required this.onTransfer});
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onTransfer,
child: const Text('Transfer'),
);
}
}
```
---
### UI-FLUTTER-03: Controller & Resource Disposal
> [!CAUTION]
> Undisposed controllers (`TextEditingController`, `AnimationController`, `ScrollController`, `FocusNode`) leak listeners and retain the entire widget element tree in memory indefinitely.
```dart
// ❌ CRITICAL - Leaking controller when widget is removed from tree
class InputWidget extends StatefulWidget {
const InputWidget({super.key});
@override
State createState() => _InputWidgetState();
}
class _InputWidgetState extends State {
final _controller = TextEditingController();
final _focusNode = FocusNode();
// ❌ Missing dispose() method! Memory leak!
}
// ✅ CORRECT - Deterministic resource cleanup in dispose()
class _InputWidgetState extends State {
late final TextEditingController _controller;
late final FocusNode _focusNode;
@override
void initState() {
super.initState();
_controller = TextEditingController();
_focusNode = FocusNode();
}
@override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
}
```
---
### UI-FLUTTER-04: Theming & Design System Token Adherence
Never hardcode hex colors or custom font sizes directly in widgets. Always use centralized theme tokens (`Theme.of(context)` or `AppColors`).
```dart
// ❌ BAD - Hardcoded hex colors break dynamic branding and Dark Mode
Container(
color: const Color(0xFF1E88E5), // ❌ Hardcoded color
child: const Text(
'Balance',
style: TextStyle(fontSize: 16, color: Color(0xFF000000)), // ❌ Hardcoded style
),
)
// ✅ CORRECT - Themed tokens from ThemeData / ThemeTailor
Container(
color: Theme.of(context).colorScheme.primary,
child: Text(
'Balance',
style: Theme.of(context).textTheme.titleMedium,
),
)
```
---
### UI-FLUTTER-05: Layout Hygiene & Overflow Protection
Layouts must guard against screen size variations, orientation shifts, and soft keyboard appearance.
```dart
// ❌ BAD - Direct Column causes RenderFlex overflow on small screens or when keyboard opens
class LoginForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column( // ❌ May overflow with keyboard
children: [
TextField(),
TextField(),
ElevatedButton(...),
],
);
}
}
// ✅ CORRECT - SafeArea and SingleChildScrollView protect against layout overflows
class LoginForm extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const TextField(),
const SizedBox(height: 16),
const TextField(),
const SizedBox(height: 24),
ElevatedButton(...),
],
),
),
);
}
}
```
---
## 📥 Input Specifications
This skill accepts:
1. **Git Diff**: A diff stream containing Flutter Widget `.dart` files (`git diff origin/develop...HEAD`).
2. **Commit ID**: A specific commit hash (`git show `).
3. **File Path**: Direct path to any Widget or Screen file (`packages/ui_kit/lib/widgets/...` or `features/.../presentation/...`).
---
## 📤 Output Format
Your audit response **MUST** follow this standardized structure:
```markdown
### 🎨 Flutter UI Audit Report
**Audit Status**: ✅ PASSED / ❌ FAILED (Blockers Found)
#### 📋 Flutter UI Performance & Design Check Table
| Check Area | Status | Target Widget / Area | Details |
| :--- | :--- | :--- | :--- |
| **UI-FLUTTER-01: Rebuild & const** | ✅ / ❌ | | const constructors, buildWhen isolation |
| **UI-FLUTTER-02: State Hoisting** | ✅ / ❌ | | Dumb widgets, BLoC event delegation |
| **UI-FLUTTER-03: Controller Disposal** | ✅ / ❌ | | All controllers & focus nodes disposed |
| **UI-FLUTTER-04: Theme Token Adherence** | ✅ / ❌ | | Theme.of(context), zero hardcoded colors |
| **UI-FLUTTER-05: Layout & Overflow Safety**| ✅ / ❌ | | SafeArea, SingleChildScrollView |
#### 🚨 Flutter UI Blockers (🔴 Must Fix Immediately)
- **[File:Line]**: [Rebuild / Disposal / Hoisting defect] → [Required remediation]
#### 💡 Flutter UI Optimizations (🟡 Suggestions)
- **[File:Line]**: [Theming or layout enhancement recommendation]
```