A production-ready Flutter starter kit using MVVM architecture with Provider for state management and GetIt for dependency injection. Built to jumpstart new projects without creating everything from scratch.
┌─────────────────────────────────────────────────────────┐
│ Presentation Layer │
│ (Pages, Widgets) │
│ Uses ChangeNotifierProvider / Consumer / Selector │
└──────────────────────┬──────────────────────────────────┘
│ reads / notifies
┌──────────────────────▼──────────────────────────────────┐
│ ViewModel Layer │
│ (ChangeNotifier) │
│ Business logic, state management, navigation │
└──────────────────────┬──────────────────────────────────┘
│ calls
┌──────────────────────▼──────────────────────────────────┐
│ Repository Layer │
│ (Abstract Repo + RepoImpl) │
│ Returns Either<Failure, GenericApiModel<T>> │
└──────────────────────┬──────────────────────────────────┘
│ uses
┌──────────────────────▼──────────────────────────────────┐
│ Data Layer │
│ (ApiClient, CacheService, SharedPreferences) │
└─────────────────────────────────────────────────────────┘
flutter_clean_arch_starter/
├── lib/
│ ├── core/ # Core app files
│ │ ├── api/
│ │ │ ├── api_client.dart # Abstract API client (post/get with callbacks)
│ │ │ └── http_client_manager.dart # HTTP wrapper with timeout
│ │ ├── common_models/
│ │ │ └── generic_api_model.dart # Generic API response wrapper<T>
│ │ ├── exceptions/
│ │ │ ├── api_exception.dart # Exception wrapper
│ │ │ └── api_failure.dart # Failure hierarchy (Equatable)
│ │ ├── utils/
│ │ │ ├── loading_utils.dart # Full-screen loader
│ │ │ ├── size_utils.dart # Sizer extensions
│ │ │ ├── utils.dart # Common utilities
│ │ │ └── validation_utils.dart # Form validators
│ │ ├── api_consts.dart # API URLs & endpoints
│ │ ├── app_assets.dart # Asset path constants
│ │ ├── app_export.dart # Barrel exports
│ │ ├── app_state.dart # Global app state (auth, tokens)
│ │ ├── cache_consts.dart # SharedPreferences keys
│ │ ├── dependencies.dart # GetIt DI setup
│ │ └── enums.dart # App-wide enums
│ ├── extensions/
│ │ ├── context_extensions.dart # Theme, colors, navigation
│ │ ├── string_extensions.dart # String manipulations
│ │ └── widget_extensions.dart # Padding, center, expand
│ ├── features/ # Feature modules (MVVM)
│ │ └── authentication/ # Example feature
│ │ ├── authentication_repo.dart # Abstract + Impl
│ │ └── authentication_view_model.dart # ChangeNotifier
│ ├── presentation/ # UI pages
│ │ ├── authentication/
│ │ │ └── login_page.dart # Login screen
│ │ └── home/
│ │ └── home_page.dart # Home screen
│ ├── routes/
│ │ └── app_routes.dart # Named routes
│ ├── services/ # App services (DI)
│ │ ├── analytics_service.dart # Analytics (no-op impl)
│ │ ├── cache_service.dart # SharedPreferences wrapper
│ │ ├── dialog_service.dart # Dialogs, bottom sheets, snackbars
│ │ ├── error_handler_service.dart # Failure → user action mapping
│ │ ├── logger_service.dart # Logger wrapper
│ │ └── navigation_service.dart # Navigator key + navigation
│ ├── theme/
│ │ ├── app_theme.dart # Light & dark ThemeData
│ │ └── colors.dart # Color palette + dark mode
│ ├── widgets/ # Reusable widgets
│ │ ├── custom_button.dart # Elevated/Outlined button
│ │ ├── custom_text_field.dart # Form text field
│ │ └── loading_widget.dart # Loader + Shimmer
│ └── main.dart # Entry point
├── assets/
│ ├── animations/lottie/ # Lottie JSON files
│ ├── fonts/ # Font files (Poppins)
│ ├── langs/ # Localization JSON
│ │ ├── en-US.json
│ │ └── ar-SA.json
│ ├── png/ # PNG images
│ └── svg/ # SVG images
├── test/
├── pubspec.yaml
├── analysis_options.yaml
├── .gitignore
└── README.md
- Flutter SDK >= 3.6.0
- Dart SDK >= 3.6.0
cd flutter_clean_arch_starter
flutter pub getThis starter kit contains only the lib/ and assets/ structure. Generate the android/ and ios/ folders:
flutter create --org com.yourcompany --project-name flutter_clean_arch_starter .Edit lib/core/api_consts.dart:
- Set
appEnvironmentTypetodev,uat,staging, orprod - Update
baseUrland endpoint paths
flutter run| Category | Package | Purpose |
|---|---|---|
| State Management | provider |
ChangeNotifier-based state management |
| DI | get_it |
Service locator / dependency injection |
| Networking | http |
HTTP client |
| Network Check | connectivity_plus |
Internet connectivity check |
| Storage | shared_preferences |
Local key-value storage |
| Functional | dartz |
Either<Failure, T> for error handling |
| Value Equality | equatable |
Failure class equality |
| Logging | logger |
Pretty-printed console logs |
| Localization | easy_localization |
i18n with JSON files |
| Images | cached_network_image |
Image caching |
| SVG | flutter_svg |
SVG rendering |
| Responsive | sizer |
Screen-responsive sizing |
| UI | shimmer |
Loading placeholders |
| UI | auto_size_text |
Auto-scaling text |
| UI | fluttertoast |
Toast messages |
| UI | flutter_rating_bar |
Star ratings |
| Utilities | url_launcher |
Open external links |
| Utilities | path_provider |
File system paths |
| Utilities | device_info_plus |
Device info |
Follow the MVVM pattern demonstrated in authentication:
lib/features/your_feature/
├── your_feature_repo.dart
├── your_feature_view_model.dart
└── models/
├── request/
└── response/
abstract class YourFeatureRepo {
Future<Either<Failure, GenericApiModel<dynamic>>> getData();
}
class YourFeatureRepoImp implements YourFeatureRepo {
final ApiClient apiClient;
final LoggerService loggerService;
YourFeatureRepoImp({required this.apiClient, required this.loggerService});
@override
Future<Either<Failure, GenericApiModel<dynamic>>> getData() async {
// Call apiClient.post() and return Either
}
}class YourFeatureViewModel extends ChangeNotifier {
final YourFeatureRepo repo;
final ErrorHandlerService errorHandlerService;
YourFeatureViewModel({required this.repo, required this.errorHandlerService});
// Add state, methods, notifyListeners()
}In lib/core/dependencies.dart:
getIt.registerLazySingleton<YourFeatureRepo>(
() => YourFeatureRepoImp(apiClient: getIt(), loggerService: getIt()),
);
getIt.registerLazySingleton<YourFeatureViewModel>(
() => YourFeatureViewModel(repo: getIt(), errorHandlerService: getIt()),
);class YourFeaturePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<YourFeatureViewModel>(
create: (_) => getIt<YourFeatureViewModel>(),
child: Scaffold(
body: Consumer<YourFeatureViewModel>(
builder: (context, vm, _) {
// Build UI
},
),
),
);
}
}In lib/routes/app_routes.dart:
static const String yourFeaturePage = '/yourFeaturePage';
// In routes map:
yourFeaturePage: (context) => YourFeaturePage(),final result = await repo.getData();
result.fold(
(failure) => errorHandlerService.handleError(failure: failure),
(apiResponse) => // Handle success
);// Using BuildContext extension (recommended):
Container(color: context.primaryColor);
Text('Hello', style: TextStyle(color: context.textColor));
// Using static getters:
Container(color: AppColors.primaryColor);// Using Sizer extensions:
SizedBox(height: 2.h); // 2% of screen height
Text('Hello', style: TextStyle(fontSize: 14.sp));navigationService.pushNamed(AppRoutes.yourFeaturePage);
navigationService.pushNamedAndRemoveUntil(AppRoutes.landingScreen, (r) => false);Add translations to assets/langs/en-US.json and ar-SA.json:
{
"yourKey": "Your translation"
}Generate locale keys (optional):
flutter pub run easy_localization:generate -S assets/langs -f keys -o locale_keys.g.dartThis project is open source. Feel free to use it for your projects.