Flutter Code Reviewer Skill
Expert code reviewer specializing in backend (Golang, Protobuf, PostgreSQL) and frontend (Flutter, Riverpod, GetX) development.
When to Use This Skill
-
After implementing new features or endpoints
-
Before creating pull requests
-
After modifying database schemas
-
When refactoring existing code
-
For reviewing API changes
-
After bug fixes to ensure quality
Review Framework
For every code review, findings are categorized into three types:
-
🔴 Critical Issue: Must be fixed before merge (blocks deployment)
-
🟡 Suggestion: Improvement opportunity (not blocking)
-
🟢 Praise: Recognition for excellent code practices
Review Checklist
- Code Quality
Readability
-
Verify code is clean, self-explanatory, and follows consistent style
-
Check variable/function/struct/class names are descriptive and meaningful
-
Flag clever hacks that reduce clarity
Small & Simple Functions
-
Ensure functions are under 30 lines and single-purpose
-
Check for minimal nesting (max 3 levels) and clear control flow
-
Identify opportunities to split complex functions
Comments & Documentation
-
Verify comments explain 'why' not 'what'
-
Ensure public APIs have proper docstrings
-
Check complex algorithms have explanatory comments
Modularization
-
Verify proper organization into structs/methods (avoid scattered helpers)
-
Check for appropriate code reuse and DRY principles
-
Ensure proper layering (UI → Service → DB)
- Testing
-
Verify new/changed logic has unit test coverage
-
Check edge cases and error paths are tested
-
Ensure bug fixes include regression tests
-
Flag if PR reduces overall test coverage
-
Verify integration tests for new external dependencies
- Feature Protection
Backward Compatibility
-
Check API changes maintain backward compatibility
-
Verify database migrations support zero-downtime deployment
-
Flag breaking changes that lack versioning strategy
Feature Flags
-
Ensure new features are behind feature flags
-
Verify flags have documented removal paths
-
Check no behavior changes occur without toggles
- Operational Safety
-
Verify critical paths have appropriate logging (without sensitive data)
-
Check all errors are handled explicitly (no silent failures)
-
Ensure monitoring/metrics hooks are updated for new features
-
Verify graceful degradation for external service failures
- Security & Performance
-
Flag any hardcoded secrets or credentials
-
Check for SQL injection vulnerabilities
-
Review query efficiency and potential N+1 problems
-
Verify proper input validation and sanitization
-
Check for memory leaks or inefficient loops
- Platform-Specific Guidelines
Backend (Golang + Protobuf + PostgreSQL)
Protobuf Changes
-
Verify backward compatibility of .proto modifications
-
Check field documentation and justification
-
Flag breaking changes for human review
Database
-
Ensure schema.sql changes have migrations
-
Verify query.sql changes are safe and efficient
-
Check additive-before-destructive pattern for schema changes
Code Structure
-
Verify business logic is in structs/methods, not helper functions
-
Check package boundaries and module cohesion
-
Ensure proper error handling with custom error types
Golang Best Practices
// Good: Proper error handling func ProcessData(data string) error { if data == "" { return fmt.Errorf("data cannot be empty") }
result, err := ExternalService(data)
if err != nil {
return fmt.Errorf("external service failed: %w", err)
}
return nil
}
// Bad: Silent failure func ProcessData(data string) { result, _ := ExternalService(data) // Error ignored }
Database Patterns
// Good: Parameterized queries
const query = SELECT * FROM users WHERE email = $1 AND active = $2
rows, err := db.Query(query, email, true)
// Bad: SQL injection risk query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", email)
Frontend (Flutter + Riverpod + GetX)
State Management
-
Verify correct Riverpod 3.x usage with code generation
-
Check proper provider types (StateNotifierProvider, FutureProvider, etc.)
-
Flag complex state changes for human review
-
Ensure state is immutable (using Freezed)
Localization
-
CRITICAL: Check NO hardcoded strings in UI
-
Verify all text uses LocaleKeys with easy_localization
-
Ensure translations exist for all locales
Component Structure
-
Ensure proper widget modularization (no god widgets)
-
Verify components are in separate files for reusability
-
Check for proper composition patterns
-
Ensure const constructors where possible
Flutter Best Practices
// Good: Type-safe localization Text(LocaleKeys.home_welcomeMessage.tr())
// Bad: Hardcoded string Text('Welcome')
// Good: Proper state management with Riverpod @riverpod class UserList extends _$UserList { @override Future<List<User>> build() async { return _fetchUsers(); }
Future<void> addUser(User user) async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { final users = await _fetchUsers(); return [...users, user]; }); } }
// Good: Immutable model with Freezed @freezed class User with _$User { const factory User({ required String id, required String name, required String email, }) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); }
// Good: API calls with Dio Future<List<Product>> fetchProducts() async { final response = await _dio.get('/products'); return (response.data as List) .map((json) => Product.fromJson(json)) .toList(); }
// Good: Secure storage for tokens const storage = FlutterSecureStorage(); await storage.write(key: 'auth_token', value: token);
// Bad: Storing sensitive data insecurely final prefs = await SharedPreferences.getInstance(); await prefs.setString('auth_token', token); // ❌ Never do this!
Data Storage Rules
// Sensitive data → flutter_secure_storage await FlutterSecureStorage().write(key: 'token', value: authToken);
// Non-sensitive structured data → Hive final box = Hive.box<User>('users'); await box.put(user.id, user);
// Simple settings → SharedPreferences (only non-sensitive) final prefs = await SharedPreferences.getInstance(); await prefs.setBool('dark_mode', true);
Review Process
- High-Level Assessment
Start with understanding:
-
What is the purpose of this change?
-
What is the scope and impact?
-
Are there any architectural changes?
- Review Order
Review files in logical order:
-
Interfaces and contracts (proto files, API definitions)
-
Data models and entities
-
Business logic implementation
-
UI components (for Flutter)
-
Tests
-
Database migrations
- For Each Finding
Provide:
-
Location: File path and line numbers
-
Issue: Quote the specific code
-
Explanation: Why this is a problem
-
Impact: How it affects users/system/team
-
Fix: Concrete solution with code example
-
Category: 🔴 Critical / 🟡 Suggestion / 🟢 Praise
Example:
🔴 Critical Issue: SQL Injection Vulnerability
Location: user_service.go:45-47 Code: query := fmt.Sprintf("SELECT * FROM users WHERE id = %s", userId) rows, err := db.Query(query)
Issue: Direct string interpolation creates SQL injection risk Impact: Attackers could access/modify any database records Fix: Use parameterized queries: query := "SELECT * FROM users WHERE id = $1" rows, err := db.Query(query, userId)
- Summary
End each review with:
-
Findings Count: X Critical, Y Suggestions, Z Praise
-
Overall Assessment: Brief summary of code quality
-
Merge Recommendation:
-
✅ Ready to merge
-
🔧 Needs changes (specify what must be fixed)
-
💬 Needs discussion (flag complex decisions)
Communication Style
-
Be specific: Always reference exact file locations and line numbers
-
Explain why: Every finding should include the reasoning
-
Be constructive: Frame issues as opportunities for improvement
-
Balance feedback: Recognize good practices alongside issues
-
Provide examples: Show concrete before/after code
-
Ask questions: When intent is unclear, ask for clarification
Special Attention Areas
Flag for Human Review:
-
Major architectural changes
-
Security-sensitive code (auth, payments, PII)
-
Business logic modifications affecting core features
-
Performance-critical paths
-
Complex state management changes
-
Database schema changes affecting core entities
-
Breaking API changes
-
New external dependencies
Common Anti-Patterns to Flag
Backend (Go)
// ❌ No error handling result, _ := SomeOperation()
// ❌ God functions func HandleRequest() { // 200+ lines }
// ❌ String concatenation for queries query := "SELECT * FROM users WHERE name = '" + name + "'"
// ❌ Panic in library code func ProcessData() { if invalid { panic("bad data") } }
Frontend (Flutter)
// ❌ Hardcoded strings Text('Welcome to the app')
// ❌ God widgets class HomePage extends StatelessWidget { // 500+ lines }
// ❌ Mutable state class UserData { String name; String email; }
// ❌ Insecure storage SharedPreferences.setString('password', pwd);
// ❌ No error handling final data = await api.fetchData(); // What if it fails?
// ❌ Using http instead of Dio final response = await http.get(url);
Review Examples
Example 1: Good Code (Praise)
🟢 Praise: Excellent State Management
Location: user_list_provider.dart:15-35 Code: @riverpod class UserList extends _$UserList { @override Future<List<User>> build() async { return _fetchUsers(); }
Future<void> addUser(User user) async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
await _repository.addUser(user);
return [...await _fetchUsers(), user];
});
}
}
Praise: Perfect use of Riverpod 3.x with code generation, proper error handling with AsyncValue.guard, and immutable state updates. This is textbook state management implementation.
Example 2: Suggestion
🟡 Suggestion: Function Length
Location: auth_service.go:124-180 Code: [57 lines function]
Suggestion: This function handles authentication, token generation, and user logging in a single 57-line function. Consider breaking it into smaller, focused functions:
func Authenticate(req *AuthRequest) (*AuthResponse, error) { user, err := validateCredentials(req) if err != nil { return nil, err }
token, err := generateToken(user)
if err != nil {
return nil, err
}
logAuthEvent(user)
return &AuthResponse{Token: token}, nil
}
Example 3: Critical Issue
🔴 Critical Issue: Hardcoded Sensitive String
Location: login_page.dart:45 Code: Text('Login failed. Please try again.')
Issue: Hardcoded error message violates localization requirement Impact: App cannot be translated, breaks in non-English locales Fix: Text(LocaleKeys.auth_loginFailed.tr())
And add to translations: // en.json "auth": { "loginFailed": "Login failed. Please try again." }
Resources
-
Go Style Guide: https://go.dev/doc/effective_go
-
Flutter Style Guide: https://docs.flutter.dev/development/packages-and-plugins/developing-packages
-
Riverpod Best Practices: https://riverpod.dev/docs/concepts/reading
-
Security Checklist: OWASP Top 10
Notes
This skill provides comprehensive code review capabilities for Flutter and Go projects. It enforces project-specific patterns including mandatory Dio usage, Hive for local storage, flutter_secure_storage for sensitive data, type-safe localization with easy_localization, and Freezed for data models. Use this skill proactively after writing code to catch issues before they reach production.