Claude-skill-registry flutter-reviewer
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/flutter-reviewer" ~/.claude/skills/majiayu000-claude-skill-registry-flutter-reviewer && rm -rf "$T"
manifest:
skills/data/flutter-reviewer/SKILL.mdsource content
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
containing flutter dependencypubspec.yaml
Project Detection
withpubspec.yaml
dependencyflutter:
existslib/main.dart
andandroid/
directories presentios/
files with Flutter imports.dart
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 |
// 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 |
// 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 |
// 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() |
// 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 |
// 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
- Widgets: Use const, extract small widgets, add keys
- State: Choose appropriate state management, dispose resources
- Performance: Avoid rebuilds, use isolates for heavy work
- Platform: Handle errors, use typed method channels
- Testing: Widget tests, golden tests, integration tests
Integration
skill: General Dart code qualitycode-reviewer
skill: Flutter test generationtest-generator
skill: Platform channel Android sidekotlin-android-reviewer
skill: Platform channel iOS sideios-reviewer
Notes
- Based on Flutter 3.x, Dart 3.x with null safety
- Supports BLoC, Provider, Riverpod, GetX patterns
- Includes platform channel best practices