Back to list
physics91

flutter-reviewer

by physics91

Claude Code hooks를 활용하여 세션 컴팩션 시 AGENTS.md 컨텍스트를 자동으로 보존/복원하는 플러그인

0🍴 0📅 Dec 29, 2025

SKILL.md


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

CheckRecommendationSeverity
Heavy logic in build()Move to initState or separate methodHIGH
Missing const constructorAdd const for immutable widgetsHIGH
Unnecessary setStateUse state management for complex stateMEDIUM
Large build methodExtract to smaller widgetsMEDIUM
Missing key in ListViewAdd key for correct item trackingHIGH
// 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

CheckRecommendationSeverity
BLoC without closeMemory leak riskCRITICAL
emit after closeRuntime error riskHIGH
Nested BlocBuilderUse BlocSelector or MultiBlocListenerMEDIUM
Business logic in UIMove to BLoCMEDIUM
// 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

CheckRecommendationSeverity
Provider not disposedMemory leakHIGH
ChangeNotifier without notifyListenersUI not updatedHIGH
context.read in buildUse context.watchHIGH
Missing ProviderScopeRiverpod won't workCRITICAL
// 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

CheckProblemSolution
Unnecessary rebuildsPerformanceconst, shouldRebuild, select
Heavy imagesMemory/performancecached_network_image, resize
Sync file I/OUI jankUse compute() or Isolate
AnimationController not disposedMemory leakDispose 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

CheckRecommendationSeverity
Missing null checkCrash on nullHIGH
No error handlingSilent failuresMEDIUM
Main thread blockingANR/UI freezeHIGH
Hard-coded channel nameMaintenance issueLOW
// 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

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon