Feature Generator π οΈ
A command-line interface (CLI) tool that accelerates Flutter development by generating Clean Architecture folder structures with boilerplate code for BLoC/Cubit state management.
Table of Contents π
- Installation
- Usage
- Generated Structure
- Example
- Dependencies
- Configuration
- Troubleshooting
- Contributing
- License
Installation π»
Install globally using Dart:
1. Downloaded it from pub.flutter-io.cn
then run this code at terminal:
dart pub global activate feature_generator
For Bash/Zsh
export PATH="$PATH:$HOME/.pub-cache/bin"
For PowerShell
$env:Path += ";$env:USERPROFILE\.pub-cache\bin"
Usage π
Generate a feature structure with optional automatic dependency installation:
At first , run :
feature_generator create --name <FEATURE_NAME> --install-deps
Then:
feature_generator create --name <FEATURE_NAME>
Example:
Core folders (lib/core/) are only created when using the --install-deps flag:
- First run With full automatic installation for installing the used packages
feature_generator create --name Auth --install-deps
- After that use
feature_generator create --name Auth
Generated Structure π³
βββ core/ # Shared project components
β βββ errors/ # Custom error classes
β β βββ failure.dart # Failure type definitions
β βββ use_cases/ # Base use case classes
β βββ use_case.dart # Abstract UseCase template
β
βββ features/ # Feature modules
βββ <feature_name>/ # Generated feature name
βββ data/
β βββ data_sources/ # API/Remote data sources
β βββ models/ # Data model classes
β βββ repo/ # Repository implementations
β
βββ domain/
β βββ repositories/ # Abstract repository contracts
β βββ use_cases/ # Business logic components
β
βββ presentation/
βββ controller/ # BLoC/Cubit + State classes
βββ views/
βββ screens/ # Full page views
βββ widgets/ # Reusable components
The lib/ directory is divided into two main sections: shared utilities (core/) and feature-specific modules (features/). Below is a breakdown of the structure in a tabular format:
| Directory Path | Purpose |
|---|---|
core/errors/failure.dart |
Defines custom error types for the app. |
core/use_cases/use_case.dart |
Abstract template for use case classes. |
features/<feature_name>/data/data_sources/ |
Handles API or remote data interactions. |
features/<feature_name>/data/models/ |
Contains data model classes for serialization. |
features/<feature_name>/data/repo/ |
Implements data repository logic. |
features/<feature_name>/domain/repositories/ |
Defines abstract repository interfaces. |
features/<feature_name>/domain/use_cases/ |
Encapsulates business logic for the feature. |
features/<feature_name>/presentation/controller/ |
Manages state using BLoC or Cubit. |
features/<feature_name>/presentation/views/screens/ |
Full-page UI views for the feature. |
features/<feature_name>/presentation/views/widgets/ |
Reusable UI components. |
Key additions:
- Core directory structure shown at project root level
- Explicit paths for critical base files
- Clear separation between shared core components and feature modules
The core directory will be generated once during the first feature creation. Subsequent features will reuse these core components.
Example Core Files section added: markdown
Core Components π¨
Failure Class (lib/core/errors/failure.dart)
abstract class Failure {
final String message;
const Failure(this.message);
}
class ServerFailure extends Failure {
ServerFailure(String message) : super(message);
}
Example Code π§π»
1. Cubit File (user_profile_cubit.dart):
@injectable
class UserProfileCubit extends Cubit<UserProfileState> {
final FetchUserProfileUseCase fetchUserProfileUseCase;
UserProfileCubit(this.fetchUserProfileUseCase)
: super(UserProfileInitial());
Future<void> loadProfile() async {
emit(UserProfileLoading());
// ... cubit logic
}
2. Repository Contract (user_profile_repository.dart):
abstract class UserProfileRepository {
Future<Either<Failure, UserProfileModel>> getProfile();
}
3. UseCase Template (lib/core/use_cases/use_case.dart):
import 'package:dartz/dartz.dart';
import '../Errors/failure.dart';
abstract class UseCases<Type> {
Future<Either<Failure, Type>> call();
}
abstract class UseCasesWithParamater<Type, Parameter> {
Future<Either<Failure, Type>> call(Parameter parameter);
}
4. Failure (lib/core/errors/failure.dart) :
import 'package:dio/dio.dart';
class Failure {
final String message;
Failure({required this.message});
}
class ServerFailure extends Failure {
ServerFailure({required super.message});
factory ServerFailure.fromBadResponse(Response response) {
if (response.statusCode == 404) {
return ServerFailure(
message: 'Your request was not found, Please try later');
} else if (response.statusCode == 500) {
return ServerFailure(
message: 'There are errors with server, Please try later');
} else if (response.statusCode == 400 ||
response.statusCode == 401 ||
response.statusCode == 403) {
return ServerFailure(message: response.statusMessage.toString());
} else {
return ServerFailure(message: 'Please try later');
}
}
factory ServerFailure.fromDioException(DioException e) {
switch (e.type) {
case DioExceptionType.connectionTimeout:
return ServerFailure(message: 'Connection timed out');
case DioExceptionType.sendTimeout:
return ServerFailure(message: 'Connection send timed out');
case DioExceptionType.receiveTimeout:
return ServerFailure(message: 'Connection received timed out');
case DioExceptionType.badCertificate:
return ServerFailure(message: 'Bad certification error');
case DioExceptionType.badResponse:
return ServerFailure.fromBadResponse(e.response!);
case DioExceptionType.cancel:
return ServerFailure(message: 'Connection canceled');
case DioExceptionType.connectionError:
return ServerFailure(message: 'Connection Error');
case DioExceptionType.unknown:
return ServerFailure(message: 'Unknown Error');
}
}
}
5. Data Source (featurs/FeatureName/data/data_sources/featurename_data_source.dart):
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import '/core/api_helper/api_endpoints.dart';
import '/core/api_helper/api_headers.dart';
import '/core/api_helper/api_helper.dart';
abstract class FeatureNameRemoteDataSource {
Future<FeatureNameModel> getFeatureName();
}
@Singleton(as: FeatureNameRemoteDataSource)
class FeatureNameRemoteDataSourceImplementation extends FeatureNameRemoteDataSource {
late FeatureNameModel FeatureNameModel;
late Response response;
final DioHelper dioHelper ;
FeatureNameRemoteDataSourceImplementation({required this.dioHelper});
@override
Future<FeatureNameModel> getFeatureName() async {
response = await dioHelper.getData(ApisEndPoints.kGetFeatureNameDataUrl,
headers: headersMapWithToken());
FeatureNameModel = FeatureNameModel.fromJson(response.data ?? {});
return FeatureNameModel;
}
}
6. Data RepoRepository (featurs/FeatureName/data/repo/featurename_repo.dart):
import 'package:dartz/dartz.dart';
import 'package:dio/dio.dart';
import 'package:injectable/injectable.dart';
import '/Core/Errors/failure.dart';
@Singleton(as: FEATURENAMERepository)
class FEATURENAMERepoImpl extends FEATURENAMERepository {
final FEATURENAMERemoteDataSource remoteDataSource;
FEATURENAMERepoImpl({required this.remoteDataSource});
@override
Future<Either<Failure, FEATURENAMEModel>> getFEATURENAME() async {
try {
FEATURENAMEModel request = await remoteDataSource.getFEATURENAME();
return right(request);
} on Exception catch (e) {
if (e is DioException) {
return left(ServerFailure.fromDioException(e));
} else {
return left(ServerFailure(message: e.toString()));
}
}
}
}
7. Domain Repository (featurs/FeatureName/domain/repositories/featurename_repository.dart):
import 'package:dartz/dartz.dart';
import '/Core/Errors/failure.dart';
abstract class FEATURENAMERepository {
Future<Either<Failure, FEATURENAMEModel>> getFEATURENAME();
}
7. Domain UseCases (featurs/FeatureName/domain/use_cases/featurenameuse_case.dart):
import 'package:dartz/dartz.dart';
import 'package:injectable/injectable.dart';
import '/Core/Errors/failure.dart';
import '/Core/UseCase/use_case.dart';
@lazySingleton
class FetchFEATURENAMEUseCase extends UseCases<FEATURENAMEModel> {
final FEATURENAMERepository FEATURENAMERepository;
FetchFEATURENAMEUseCase({required this.FEATURENAMERepository});
@override
Future<Either<Failure, FEATURENAMEModel>> call() async {
return await FEATURENAMERepository.getFEATURENAME();
}
}
Dependencies π¦
These dependencies will added to your pubspec.yaml:
dependencies:
flutter_bloc:
injectable:
dartz:
dio:
dev_dependencies:
build_runner:
injectable_generator:
Run after code generation:
flutter pub run build_runner build --delete-conflicting-outputs
Configuration βοΈ
Create feature_config.json for custom templates:
{
"base_path": "lib/modules",
"use_freezed": true,
"add_routing": false
}
Troubleshooting π§
Issue: Command not found
# Verify installation
dart pub global list
# Check PATH configuration
echo $PATH
Issue: Missing dependencies
flutter clean
flutter pub get
Contributing π€
-
Fork the repository
-
Create feature branch (git checkout -b feature/improve-generator)
-
Commit changes (git commit -m 'Add template customization')
-
Push to branch (git push origin feature/improve-generator)
-
Open a Pull Request
License π
This project is licensed under the MIT License - see the LICENSE file for details.
Libraries
- feature_generator Main
- A CLI tool for generating Clean Architecture feature structures
Main
Main Components