home / skills / physics91 / claude-vibe / flutter-reviewer

flutter-reviewer skill

/skills/flutter-reviewer

This skill reviews Flutter/Dart code for widget patterns, state management, performance, and platform channels to optimize quality and reliability.

npx playbooks add skill physics91/claude-vibe --skill flutter-reviewer

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
9.0 KB
---
name: flutter-reviewer
description: |
  WHEN: Flutter/Dart code review, Widget patterns, State management checks, BLoC/Provider/Riverpod analysis
  WHAT: Widget best practices + State management patterns + Performance optimization + Platform channel review
  WHEN NOT: Native Android → kotlin-android-reviewer, Native iOS → ios-reviewer
---

# Flutter Reviewer Skill

## Purpose
Reviews Flutter/Dart code for widget patterns, state management, performance, and cross-platform best practices.

## When to Use
- Flutter project code review
- "Widget", "BLoC", "Provider", "Riverpod", "GetX" mentions
- Flutter performance, rebuild optimization inspection
- Projects with `pubspec.yaml` containing flutter dependency

## Project Detection
- `pubspec.yaml` with `flutter:` dependency
- `lib/main.dart` exists
- `android/` and `ios/` directories present
- `.dart` files with Flutter imports

## Workflow

### Step 1: Analyze Project
```
**Flutter**: 3.x
**Dart**: 3.x
**State Management**: BLoC / Provider / Riverpod / GetX
**Architecture**: Clean Architecture / MVVM / MVC
**Null Safety**: Enabled
```

### Step 2: Select Review Areas
**AskUserQuestion:**
```
"Which areas to review?"
Options:
- Full Flutter pattern check (recommended)
- Widget build optimization
- State management patterns
- Platform channels/native code
- Navigation/routing patterns
multiSelect: true
```

## Detection Rules

### Widget Patterns
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Heavy logic in build() | Move to initState or separate method | HIGH |
| Missing const constructor | Add const for immutable widgets | HIGH |
| Unnecessary setState | Use state management for complex state | MEDIUM |
| Large build method | Extract to smaller widgets | MEDIUM |
| Missing key in ListView | Add key for correct item tracking | HIGH |

```dart
// BAD: Heavy logic in build
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final result = expensiveCalculation();  // Called every build
    return Text(result);
  }
}

// GOOD: Compute outside or cache
class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  late String result;

  @override
  void initState() {
    super.initState();
    result = expensiveCalculation();
  }

  @override
  Widget build(BuildContext context) => Text(result);
}

// BAD: Missing const
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Text('Hello'),  // Rebuilds unnecessarily
    );
  }
}

// GOOD: Use const
class MyWidget extends StatelessWidget {
  const MyWidget({super.key});

  @override
  Widget build(BuildContext context) {
    return const Text('Hello');  // Skipped in rebuilds
  }
}

// BAD: Missing key in ListView
ListView.builder(
  itemBuilder: (context, index) => ListTile(
    title: Text(items[index].name),
  ),
)

// GOOD: Add key
ListView.builder(
  itemBuilder: (context, index) => ListTile(
    key: ValueKey(items[index].id),
    title: Text(items[index].name),
  ),
)
```

### State Management - BLoC
| Check | Recommendation | Severity |
|-------|----------------|----------|
| BLoC without close | Memory leak risk | CRITICAL |
| emit after close | Runtime error risk | HIGH |
| Nested BlocBuilder | Use BlocSelector or MultiBlocListener | MEDIUM |
| Business logic in UI | Move to BLoC | MEDIUM |

```dart
// BAD: BLoC not closed
class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final bloc = MyBloc();

  @override
  Widget build(BuildContext context) => BlocBuilder...
  // Missing dispose!
}

// GOOD: Close BLoC
@override
void dispose() {
  bloc.close();
  super.dispose();
}

// Better: Use BlocProvider
BlocProvider(
  create: (context) => MyBloc(),
  child: MyWidget(),  // Auto-disposed
)

// BAD: Nested BlocBuilder
BlocBuilder<BlocA, StateA>(
  builder: (context, stateA) {
    return BlocBuilder<BlocB, StateB>(
      builder: (context, stateB) {
        return Text('${stateA.value} ${stateB.value}');
      },
    );
  },
)

// GOOD: Use MultiBlocListener or BlocSelector
MultiBlocListener(
  listeners: [
    BlocListener<BlocA, StateA>(...),
    BlocListener<BlocB, StateB>(...),
  ],
  child: Builder(
    builder: (context) {
      final a = context.watch<BlocA>().state;
      final b = context.watch<BlocB>().state;
      return Text('${a.value} ${b.value}');
    },
  ),
)
```

### State Management - Provider/Riverpod
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Provider not disposed | Memory leak | HIGH |
| ChangeNotifier without notifyListeners | UI not updated | HIGH |
| context.read in build | Use context.watch | HIGH |
| Missing ProviderScope | Riverpod won't work | CRITICAL |

```dart
// BAD: context.read in build (for reactive updates)
@override
Widget build(BuildContext context) {
  final value = context.read<MyProvider>().value;  // Won't rebuild!
  return Text(value);
}

// GOOD: context.watch for reactive
@override
Widget build(BuildContext context) {
  final value = context.watch<MyProvider>().value;
  return Text(value);
}

// BAD: Missing notifyListeners
class MyNotifier extends ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    // Missing notifyListeners()!
  }
}

// GOOD: Call notifyListeners
void increment() {
  _count++;
  notifyListeners();
}

// Riverpod: Missing ProviderScope
void main() {
  runApp(MyApp());  // Riverpod providers won't work!
}

// GOOD: Wrap with ProviderScope
void main() {
  runApp(
    ProviderScope(
      child: MyApp(),
    ),
  );
}
```

### Performance Optimization
| Check | Problem | Solution |
|-------|---------|----------|
| Unnecessary rebuilds | Performance | const, shouldRebuild, select |
| Heavy images | Memory/performance | cached_network_image, resize |
| Sync file I/O | UI jank | Use compute() or Isolate |
| AnimationController not disposed | Memory leak | Dispose in dispose() |

```dart
// BAD: Sync heavy operation
void loadData() {
  final data = File('large.json').readAsStringSync();
  final parsed = jsonDecode(data);  // Blocks UI!
}

// GOOD: Use compute/Isolate
Future<void> loadData() async {
  final data = await compute(parseJson, filePath);
}

// BAD: AnimationController leak
class _MyState extends State<MyWidget> with TickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this);
  }
  // Missing dispose!
}

// GOOD: Dispose controller
@override
void dispose() {
  _controller.dispose();
  super.dispose();
}
```

### Platform Channels
| Check | Recommendation | Severity |
|-------|----------------|----------|
| Missing null check | Crash on null | HIGH |
| No error handling | Silent failures | MEDIUM |
| Main thread blocking | ANR/UI freeze | HIGH |
| Hard-coded channel name | Maintenance issue | LOW |

```dart
// BAD: No error handling
Future<String> getPlatformVersion() async {
  final version = await platform.invokeMethod('getVersion');
  return version;
}

// GOOD: Error handling
Future<String> getPlatformVersion() async {
  try {
    final version = await platform.invokeMethod<String>('getVersion');
    return version ?? 'Unknown';
  } on PlatformException catch (e) {
    return 'Failed: ${e.message}';
  }
}
```

## Response Template
```
## Flutter Code Review Results

**Project**: [name]
**Flutter**: 3.x | **Dart**: 3.x
**State Management**: BLoC/Provider/Riverpod
**Files Analyzed**: X

### Widget Patterns
| Status | File | Issue |
|--------|------|-------|
| HIGH | lib/screens/home.dart | Missing const constructor (line 45) |
| HIGH | lib/widgets/list_item.dart | Missing key in ListView |

### State Management
| Status | File | Issue |
|--------|------|-------|
| CRITICAL | lib/blocs/user_bloc.dart | BLoC not closed (line 23) |
| HIGH | lib/providers/cart.dart | Missing notifyListeners |

### Performance
| Status | File | Issue |
|--------|------|-------|
| HIGH | lib/services/data.dart | Sync file I/O blocking UI |
| MEDIUM | lib/screens/gallery.dart | Large images not cached |

### Recommended Actions
1. [ ] Add const to immutable widgets
2. [ ] Close BLoCs in dispose or use BlocProvider
3. [ ] Use compute() for heavy operations
4. [ ] Add keys to ListView items
```

## Best Practices
1. **Widgets**: Use const, extract small widgets, add keys
2. **State**: Choose appropriate state management, dispose resources
3. **Performance**: Avoid rebuilds, use isolates for heavy work
4. **Platform**: Handle errors, use typed method channels
5. **Testing**: Widget tests, golden tests, integration tests

## Integration
- `code-reviewer` skill: General Dart code quality
- `test-generator` skill: Flutter test generation
- `kotlin-android-reviewer` skill: Platform channel Android side
- `ios-reviewer` skill: Platform channel iOS side

## Notes
- Based on Flutter 3.x, Dart 3.x with null safety
- Supports BLoC, Provider, Riverpod, GetX patterns
- Includes platform channel best practices

Overview

This skill reviews Flutter and Dart code for widget patterns, state management, performance, and platform channel issues. It highlights risky patterns, suggests concrete fixes, and targets BLoC, Provider, Riverpod, and other common Flutter state solutions. Use it to get actionable, prioritised recommendations on code quality and runtime safety.

How this skill works

The skill scans a Flutter project (pubspec.yaml, lib/, android/, ios/) and analyzes .dart files for build-time anti-patterns, state management lifecycle problems, performance hotspots, and platform channel safety. It reports severity-tagged issues (CRITICAL / HIGH / MEDIUM) and provides concise remediation steps and code examples. You can request focused checks (widgets, state, performance, platform channels) or a full review.

When to use it

  • Performing code review on a Flutter project (pubspec.yaml contains flutter:)
  • Auditing widget build methods for heavy logic or missing consts
  • Validating BLoC/Provider/Riverpod usage and lifecycle management
  • Investigating performance issues: rebuilds, images, I/O, animations
  • Reviewing platform channel implementations and native interop safety

Best practices

  • Keep build() lightweight: compute expensive work in initState or memoize results
  • Mark immutable widgets const and extract large build methods into smaller widgets
  • Close/dispose state objects: bloc.close(), Provider disposal, AnimationController.dispose()
  • Use context.watch/select or BlocSelector to avoid unnecessary rebuilds and nested builders
  • Offload heavy CPU or file I/O to compute()/Isolate and cache large images

Example use cases

  • Full project review to prioritize fixes before release
  • Spot-check a screen with frequent rebuilds and jank to identify root causes
  • Audit BLoC instances for missing close() calls and unsafe emits after dispose
  • Verify Riverpod setup (ProviderScope present) and ChangeNotifier notifyListeners usage
  • Review platform channel code for null checks, error handling, and main-thread blocking

FAQ

What files does the reviewer look for to detect a Flutter project?

It checks pubspec.yaml for a flutter: dependency, lib/main.dart, the presence of android/ and ios/ folders, and .dart files importing Flutter packages.

Can I request only specific checks?

Yes. You can choose targeted reviews: widget patterns, state management, performance, platform channels, or run the full pattern check.