// Expert Flutter PR reviewer for code quality, architecture, performance, and best practices. Use when reviewing pull requests, performing code audits, or ensuring Flutter/Dart code meets production standards.
| name | pr-reviewer |
| description | Expert Flutter PR reviewer for code quality, architecture, performance, and best practices. Use when reviewing pull requests, performing code audits, or ensuring Flutter/Dart code meets production standards. |
| allowed-tools | Read, Grep, Glob, Bash |
Expert code review assistance for Flutter pull requests, focusing on code quality, architecture patterns, performance, and Flutter/Dart best practices.
Before diving into details, understand the scope:
const constructors! operators)# Get list of changed files
git diff --name-only origin/main...HEAD
# Get detailed diff
git diff origin/main...HEAD
# Check for lint issues
flutter analyze
# Run tests
flutter test
# Check test coverage
flutter test --coverage
When providing review feedback, use this structure:
Brief overview of the PR and overall assessment.
Issues that must be fixed before merging:
path/to/file.dart:lineSuggested changes that would improve code quality:
path/to/file.dart:lineMinor style or preference issues:
path/to/file.dart:lineHighlight good practices observed:
WidgetName// Bad: One widget doing everything
class HomePage extends StatefulWidget {
// 500+ lines of code
}
// Good: Composed smaller widgets
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: [
const HomeHeader(),
const HomeContent(),
const HomeFooter(),
],
);
}
}
// Bad: Logic in widget
class UserProfile extends StatelessWidget {
String formatName(String first, String last) => '$first $last';
@override
Widget build(BuildContext context) {
final name = formatName(user.firstName, user.lastName);
// ...
}
}
// Good: Logic in separate class/extension
extension UserExtension on User {
String get fullName => '$firstName $lastName';
}
// Bad: No error handling
Future<void> fetchData() async {
final response = await dio.get('/api/data');
setState(() => data = response.data);
}
// Good: Proper error handling
Future<void> fetchData() async {
try {
final response = await dio.get('/api/data');
if (mounted) {
setState(() => data = response.data);
}
} on DioException catch (e) {
// Handle error appropriately
}
}
// Bad: Hardcoded strings
Text('Welcome to our app')
// Good: Use LocaleKeys
Text(LocaleKeys.home_welcome.tr())
// Bad: Direct async in initState
@override
void initState() {
super.initState();
await fetchData(); // Error: can't await in initState
}
// Good: Separate async method
@override
void initState() {
super.initState();
_loadData();
}
Future<void> _loadData() async {
await fetchData();
}
// Bad: Missing const
return Container(
padding: EdgeInsets.all(16),
child: Text('Hello'),
);
// Good: Using const where possible
return Container(
padding: const EdgeInsets.all(16),
child: const Text('Hello'),
);
This skill focuses on Flutter-specific review concerns. For state management specifics (Riverpod/BLoC), refer to the dedicated state management skills for deeper review of those patterns.