A production-ready Flutter boilerplate implementing Clean Architecture with MVVM pattern. This starter kit provides a robust, scalable, and testable foundation for building complex Flutter applications with industry best practices.
Perfect for:
- 🏢 Enterprise Applications - Scalable architecture for large teams
- 🎓 Learning Clean Architecture - Extensive comments explaining WHY, not just WHAT
- 🚀 Quick Project Kickstart - Start building features immediately
- 💼 Interview Preparation - Architecture decisions fully documented
|
|
|
|
|
|
┌─────────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Screens │ │ Providers │ │ State │ │
│ │ Widgets │ │ (ViewModels)│ │ (Freezed) │ │
│ └─────────────┘ └─────────────┘ └──────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│ depends on
┌────────────────────────▼────────────────────────────────────┐
│ Domain Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Entities │ │ Use Cases │ │ Repositories │ │
│ │ (Pure) │ │ (Logic) │ │ (Interfaces) │ │
│ └─────────────┘ └─────────────┘ └──────────────┘ │
└────────────────────────▲────────────────────────────────────┘
│ implements
┌────────────────────────┴────────────────────────────────────┐
│ Data Layer │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Models │ │ Data Sources│ │ Repositories │ │
│ │ (JSON) │ │ (API/Local) │ │ (Impl) │ │
│ └─────────────┘ └─────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
| Benefit | Description |
|---|---|
| 🧩 Separation of Concerns | Each layer has a single, well-defined responsibility |
| 🧪 Testability | Easy to test each layer in isolation |
| 🔄 Flexibility | Swap implementations without affecting other layers |
| 📈 Scalability | Patterns that scale from small to large apps |
| 👥 Team Friendly | Clear structure for team collaboration |
lib/
├── 📂 core/ # Core Infrastructure
│ ├── constants/ # API endpoints, app constants
│ ├── di/ # Dependency injection (GetIt + Injectable)
│ ├── error/ # Failures & Exceptions
│ │ ├── failures.dart # ← Sealed classes for all failures
│ │ └── exceptions.dart # ← Data layer exceptions
│ ├── network/ # HTTP client & interceptors
│ │ ├── network_client.dart # ← Dio wrapper
│ │ └── interceptors/
│ │ ├── auth_interceptor.dart # ← Token injection & 401 handling
│ │ └── logging_interceptor.dart
│ ├── router/ # Navigation (GoRouter)
│ │ ├── app_router.dart # ← Auth-aware routing
│ │ └── route_names.dart # ← Type-safe route constants
│ ├── storage/ # Local & secure storage
│ ├── types/ # Type definitions (Either, etc.)
│ └── utils/ # Logger, extensions
│
├── 📂 features/ # Feature Modules (Feature-First)
│ │
│ ├── 📂 auth/ # ✅ COMPLETE Authentication Module
│ │ ├── data/
│ │ │ ├── datasources/
│ │ │ │ ├── auth_remote_datasource.dart # API calls
│ │ │ │ └── auth_local_datasource.dart # Caching
│ │ │ ├── models/
│ │ │ │ └── user_model.dart # JSON ↔ Domain
│ │ │ └── repositories/
│ │ │ └── auth_repository_impl.dart # Implements domain interface
│ │ │
│ │ ├── domain/
│ │ │ ├── entities/
│ │ │ │ └── user.dart # Business entity
│ │ │ ├── repositories/
│ │ │ │ └── auth_repository.dart # Abstract contract
│ │ │ └── usecases/
│ │ │ ├── login_usecase.dart
│ │ │ ├── logout_usecase.dart
│ │ │ └── get_current_user_usecase.dart
│ │ │
│ │ └── presentation/
│ │ ├── providers/
│ │ │ ├── auth_provider.dart # State management
│ │ │ └── auth_state.dart # Sealed state classes
│ │ └── screens/
│ │ ├── login_screen.dart
│ │ └── splash_screen.dart
│ │
│ └── 📂 home/ # Example feature
│ └── presentation/screens/home_screen.dart
│
├── app.dart # Root widget
└── main.dart # App entry point
- Flutter SDK:
>=3.2.0 - Dart SDK:
>=3.0.0
# 1. Clone the repository
git clone https://github.com/yourusername/flutter-clean-mvvm-starter.git
cd flutter-clean-mvvm-starter
# 2. Install dependencies
flutter pub get
# 3. Run code generation (REQUIRED!)
flutter pub run build_runner build --delete-conflicting-outputs
# 4. Run the app
flutter run# Watch mode (auto-regenerate on file changes)
flutter pub run build_runner watch
# Clean build
flutter clean && flutter pub get && flutter pub run build_runner build --delete-conflicting-outputs
# Run tests
flutter test
# Code analysis
flutter analyzeA fully implemented authentication system demonstrating the entire architecture:
Features:
- Login with email/password
- Auto-login on app start
- Secure token storage
- Automatic token refresh
- Logout functionality
- User caching for offline support
Architecture Demonstration:
- ✅ Domain entities (
User) - ✅ Repository pattern (interface + implementation)
- ✅ Use cases (business logic)
- ✅ Data models with JSON serialization
- ✅ Remote & local data sources
- ✅ State management with Riverpod
- ✅ Sealed classes for state
- ✅ Type-safe error handling
No more try-catch hell! Using functional programming patterns:
// ❌ Old way - easy to forget error handling
try {
final user = await repository.login(email, password);
navigateToHome(user);
} catch (e) {
// Oops, forgot to handle this!
}
// ✅ New way - compiler forces you to handle errors
final result = await loginUseCase(email: email, password: password);
result.fold(
(failure) => failure.when(
network: () => showError('No internet connection'),
unauthorized: () => showError('Invalid credentials'),
server: (message) => showError(message),
// Compiler ensures ALL cases handled!
),
(user) => navigateToHome(user),
);Zero user friction - tokens refresh automatically:
// 1. User makes authenticated request
// 2. Token expired → 401 error
// 3. AuthInterceptor detects 401
// 4. Automatically refreshes token
// 5. Retries original request
// 6. User never notices! ✨Features:
- Request queuing during refresh
- Prevents concurrent refresh attempts
- Automatic logout on refresh failure
- Seamless user experience
Exhaustive pattern matching - never miss a state:
@freezed
class AuthState with _$AuthState {
const factory AuthState.initial() = _Initial;
const factory AuthState.loading() = _Loading;
const factory AuthState.authenticated(User user) = _Authenticated;
const factory AuthState.unauthenticated() = _Unauthenticated;
const factory AuthState.error(Failure failure) = _Error;
}
// Compiler ensures you handle ALL states
authState.when(
initial: () => SplashScreen(),
loading: () => LoadingIndicator(),
authenticated: (user) => HomeScreen(user),
unauthenticated: () => LoginScreen(),
error: (failure) => ErrorScreen(failure),
// Missing a case? → Compile error! ✅
);| Document | Description |
|---|---|
| 📘 ARCHITECTURE.md | Deep dive into architectural decisions & interview prep |
| 📗 SETUP.md | Step-by-step setup guide & troubleshooting |
| 📙 QUICKSTART.md | Quick start guide (Turkish) |
Every file includes extensive comments explaining:
- ✅ WHY this decision was made (not just WHAT)
- ✅ Alternatives considered
- ✅ Trade-offs involved
- ✅ Interview defense points
Perfect for:
- Learning Clean Architecture
- Interview preparation
- Team onboarding
- Code reviews
/// WHY RIVERPOD OVER BLOC:
/// 1. Less boilerplate (no events, just methods)
/// 2. Better compile-time safety with code generation
/// 3. Easier testing (providers are pure functions)
/// 4. Better DevTools integration
/// 5. Simpler mental model for most use cases
///
/// WHEN TO USE BLOC INSTEAD:
/// - Need explicit event tracking
/// - Team already experienced with BLoC
/// - Complex event transformation needed# Run all tests
flutter test
# Run with coverage
flutter test --coverage
# Run specific test
flutter test test/features/auth/domain/usecases/login_usecase_test.darttest/
├── core/
│ ├── network/
│ └── error/
└── features/
└── auth/
├── data/
├── domain/
└── presentation/
Follow the established pattern:
# 1. Create feature folder structure
lib/features/new_feature/
├── data/
│ ├── datasources/
│ ├── models/
│ └── repositories/
├── domain/
│ ├── entities/
│ ├── repositories/
│ └── usecases/
└── presentation/
├── providers/
├── screens/
└── widgets/
# 2. Implement from domain → data → presentation
# 3. Run code generation
flutter pub run build_runner build --delete-conflicting-outputsSee ARCHITECTURE.md for detailed guide.
- ✅ Environment Configuration - Dev, Staging, Production
- ✅ Secure Token Storage - Encrypted on device
- ✅ Offline Support - Local caching strategy
- ✅ Error Recovery - Automatic retry logic
- ✅ Logging - Comprehensive request/response logs
- ✅ Type Safety - Compile-time error checking
- ✅ Code Generation - Automated boilerplate
- ✅ Lint Rules - Production-grade code quality
- ✅ Hot Reload - Fast development cycle
- ✅ Code Organization - Feature-first structure
- ✅ Documentation - Extensive inline comments
- ✅ Examples - Complete auth module
- ✅ Tooling - VS Code + Android Studio support
- ✅ CI/CD Ready - GitHub Actions template included
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the MIT License - see the LICENSE file for details.
Q: Is this over-engineered for small apps?
Yes, for very simple apps this might be overkill. But it scales beautifully. Once you understand the pattern, adding features is actually faster than a "quick and dirty" approach that becomes unmaintainable.
Q: Why Clean Architecture instead of simpler patterns?
Clean Architecture provides:
- Clear separation of concerns
- Easy testing (mock any layer)
- Team scalability (multiple devs can work independently)
- Long-term maintainability
- Ability to swap implementations (REST → GraphQL, etc.)
Q: Can I use BLoC instead of Riverpod?
Absolutely! The architecture is framework-agnostic. Swap Riverpod providers with BLoC blocs, and the rest stays the same.
Q: How do I integrate my API?
- Update
lib/core/constants/api_constants.dartwith your endpoints - Modify
user_model.dartto match your API response - Update the auth data sources to call your endpoints
- Done! The architecture handles the rest.
- Clean Architecture by Robert C. Martin
- Riverpod Documentation
- Flutter Community
- All contributors
Built with ❤️ by the Flutter Community