2. Code Quality & Architecture
Code Quality Metrics
Error Handling Consistency60 / 100
Code Reusability55 / 100
State Management Quality70 / 100
2.1 Code Structure & Organization
Status: ⚠️Warning
Findings:
- Project uses GitFlow-like branching strategy with multiple long-lived branches
- Multiple branches exist:
master,dev,stagingwhich creates complexity - Hard to identify pending changes and what's currently deployed
- Multiple long-lived branches increase merge conflict risk and slow down development velocity
- No clear single source of truth for current production code
- Well-organized code structure with clear separation of concerns (positive aspect)
- Routes separated from route models (MVVM-like pattern)
- Clear separation between
lib/routes/,lib/route_models/,lib/states/,lib/singletons/,lib/widgets/ - Database layer properly abstracted with DAOs
Evidence:
- Git branches:
master,dev,stagingbranches exist - Multiple long-lived branches make code review and tracking difficult
- Clear directory structure:
lib/routes/- UI/Route componentslib/route_models/- ViewModels/Route modelslib/states/- State management classeslib/singletons/- Global singleton instanceslib/database/- Database layer with DAOslib/widgets/- Reusable widgets
- MVVM-like pattern with route models handling business logic
Risk Level: Medium Risk
Recommendation:
- Migrate to trunk-based development (main/master as primary branch)
- Use short-lived feature branches that merge directly to main
- Implement feature flags for incomplete features instead of long-lived branches
- Finish and merge all hanging changes from existing feature branches
- Simplify workflow: main → feature branch → PR → main → deploy
- Use branch protection rules to enforce code review before merging to main
- Consider documenting the architecture pattern in README
- Consider using dependency injection instead of singletons for better testability
2.2 Error Handling
Status: ⚠️Warning
Findings:
- Error handling is inconsistent across routes
- Some routes use
.catchError()(login_route, password_reset_verify_route, gas_receipt_route, account_four_route) - Some routes use
.onError()(register_four_route) - Some async operations may lack proper error handling
- Error messages hardcoded in Dutch, not internationalized
- Generic error messages don't provide specific feedback to users
- Error handling in Dio interceptor (token refresh) logs errors but may not provide user feedback
- Some error cases show generic dialogs without specific error information
Evidence:
lib/routes/login_route.dart: Uses.catchError()with generic error dialog (lines 287-300)- Error message: "Er ging iets mis" (hardcoded Dutch)
lib/routes/register_four_route.dart: Uses.onError()(line 353)lib/routes/password_reset_verify_route.dart: Uses.catchError()(line 302)lib/routes/gas_receipt_route.dart: Uses.catchError()(line 362)lib/routes/account_four_route.dart: Uses.catchError()in multiple places (lines 394, 562)lib/singletons/dio_singleton.dart: Token refresh error handling (lines 89-92) only resets session, doesn't notify user- Error messages hardcoded: "Er ging iets mis", "Controleer je gebruikersnaam en wachtwoord"
- No centralized error handling helper function
Risk Level: Medium Risk
Recommendation:
- Immediate actions:
- Create centralized error handling helper function
- Standardize error handling across all routes (use consistent pattern)
- Add error handling to all async operations (ensure all
.then()have error handlers) - Internationalize error messages
- Short-term:
- Create error handling base class or mixin for routes
- Add specific error types and user-friendly messages
- Implement retry mechanisms for transient errors
- Add error boundaries for widget tree errors
- Improve user feedback in Dio interceptor error handling
2.3 State Management
Status: ⚠️Warning
Findings:
- Provider pattern with
ChangeNotifierimplemented for state management - Multiple state classes follow similar patterns:
SessionState,ConnectivityState,SavingState,FavoriteGasStationState,SelectedGasStationMarker,RegisterState - Singleton pattern used for global state access
- State synchronization issues: State stored in both memory and database, potential for inconsistency
- Direct database access in some places bypasses state management layer
- Async state initialization pattern (
init()method) used inSessionState - State persistence to database happens on every state change (potential performance issue)
RegisterStatedoesn't useChangeNotifier(inconsistent pattern)- Global singleton access used throughout codebase
Evidence:
lib/states/session_state.dart: UsesChangeNotifier, stores state in both memory and database- Every setter saves to database immediately (lines 58-97)
- Async
init()method loads from database (lines 23-56)
lib/states/connectivity_state.dart: UsesChangeNotifierpatternlib/states/favorite_gas_station_state.dart: UsesChangeNotifierlib/states/saving_state.dart: UsesChangeNotifierlib/states/register_state.dart: Does NOT useChangeNotifier(inconsistent)lib/singletons/: Multiple singletons for global state accessSessionStateSingleton,RegisterStateSingleton,ConnectivityStateSingleton,DatabaseSingleton,DioSingleton,FirebaseSingleton,NavigatorSingleton
- State initialization:
sessionState.init()called separately (async pattern) - Direct database access:
lib/database/daos/accessed directly in some places
Risk Level: Medium Risk
Recommendation:
- Immediate actions:
- Review state synchronization between memory and database
- Add state validation to prevent inconsistencies
- Make
RegisterStateconsistent with other state classes (useChangeNotifier) - Consider using
flutter_secure_storagefor sensitive state instead of database
- Short-term:
- Implement state management best practices (single source of truth)
- Add state change notifications and observers
- Consider using dependency injection instead of singletons for better testability
- Separate UI state from business logic state
- Consider batching database writes for performance
2.4 Code Duplication
Status: ⚠️Warning
Findings:
- Firebase Performance tracing duplicated: Similar tracing code duplicated across all routes (30+ routes)
- Route setup pattern repeated: Similar
setup()async method pattern in multiple routes - FutureBuilder pattern duplicated: Similar FutureBuilder usage across routes for loading states
- Singleton access pattern: Similar singleton instantiation patterns across routes
- Error handling duplicated: Similar error handling code in multiple routes
- Dialog patterns repeated: Similar AlertDialog creation code in multiple places
- Session state checks: Similar session state access patterns throughout routes
- API call patterns: Similar
.then().catchError()patterns repeated across routes
Evidence:
- Firebase Performance tracing duplicated in 30+ routes:
lib/routes/login_route.dart(lines 47-52)lib/routes/generic_route.dart(lines 29-34)lib/routes/webview_route.dart(lines 37-42)- And many more routes with identical pattern
- Similar
setup()async method pattern:lib/routes/login_route.dart(lines 57-70)lib/routes/generic_route.dart(lines 45-49)lib/routes/register_one_route.dart,register_three_route.dart,register_four_route.dart- Multiple routes follow same pattern
- Similar FutureBuilder patterns for loading states across routes
- Singleton access:
SessionStateSingleton(),RegisterStateSingleton(), etc. instantiated in multiple routes - Error handling: Similar
.catchError()with AlertDialog patterns in multiple routes
Risk Level: Medium Risk
Recommendation:
- Immediate actions:
- Extract Firebase Performance tracing into a base route class or mixin
- Create reusable widgets for common UI patterns (loading states, error states)
- Extract common route patterns into base classes or mixins
- Create base route class with common functionality (
setup(), tracing, etc.)
- Short-term:
- Refactor state management classes to use inheritance or composition
- Extract common API call patterns into service classes
- Create reusable error handling widgets
- Implement route factory pattern for common route structures
- Create mixin for Firebase Performance tracing
2.5 Dead Code & Unused Files
Status: ⚠️Warning
Findings:
- Deprecated route file present in repository
lib/routes/register_two_route_old.dartis marked as@deprecatedbut still in codebase- Old route file may cause confusion and should be removed if no longer used
- Repository contains potentially unused code that should be audited
Evidence:
lib/routes/register_two_route_old.dart: Marked with@deprecatedannotation (line 15)- File still exists in repository but may not be referenced
- Similar pattern may exist in other files
Risk Level: Low Risk
Recommendation:
- Remove
lib/routes/register_two_route_old.dartif no longer used - Audit repository for other deprecated or unused files
- Establish process for removing obsolete files during refactoring
- Keep repository clean by removing dead code
- Use deprecation warnings in code to track removal timeline
2.6 Best Practices & Patterns
Status: ⚠️Warning
Findings:
- Singleton pattern used extensively, which can make testing difficult
- Direct database access in some places bypasses state management
- Inconsistent state management patterns (
RegisterStatedoesn't useChangeNotifier) - No clear dependency injection strategy
- Mix of patterns (singletons, Provider, direct database access) creates complexity
- Need strategic approach to refactoring for better testability and maintainability
Evidence:
- Multiple singleton classes in
lib/singletons/ - Direct database access in routes and models
- Inconsistent state management (most use
ChangeNotifier,RegisterStatedoesn't) - No dependency injection framework in use
- Global state access throughout codebase
Risk Level: Medium Risk
Recommendation:
- Strategic Approach: Consider migrating from singletons to dependency injection
- Align state management patterns (make all state classes consistent)
- Create clear architecture guidelines for new code
- Consider using a DI framework (e.g.,
get_it) for better testability - Document architectural decisions and patterns
- Plan refactoring sprints to improve testability and maintainability
- Separate concerns: business logic, state management, and UI layers