This document provides detailed information about creating and managing features using CleanForge Feature Brick.
← Back to Main Documentation | Hooks Guide → |
CleanForge automatically:
Example of auto-generated route configuration:
class AppPages {
static final routes = [
GetPage(
name: Routes.HOME,
page: () => const HomePage(),
binding: HomeBinding(),
),
// New routes automatically added here
];
}
Example of auto-generated theme setup:
// Theme controller with automatic persistence
class ThemeController extends GetxController {
static Rx<ThemeMode> currentTheme = ThemeMode.system.obs;
static void toggleTheme() {
currentTheme.value = currentTheme.value == ThemeMode.light
? ThemeMode.dark
: ThemeMode.light;
}
// ... other code
}
Ready-to-use theme switch widget:
class ThemeSwitchIcon extends StatelessWidget {
const ThemeSwitchIcon({super.key});
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(
ThemeController.isLightMode ? AppIcons.darkMode : AppIcons.lightMode,
color: Theme.of(context).iconTheme.color,
),
onPressed: () {
ThemeController.toggleTheme();
Get.changeThemeMode(ThemeController.currentTheme.value);
},
tooltip: Get.isDarkMode ? 'Switch to Light Mode' : 'Switch to Dark Mode',
);
}
}
}
Theme configuration with predefined styles:
class AppTheme {
static ThemeData lightTheme = ThemeData(
// Pre-configured light theme settings
);
static ThemeData darkTheme = ThemeData(
// Pre-configured dark theme settings
);
}
Each feature follows Clean Architecture principles and is organized as follows:
graph TD
subgraph "Feature Structure"
P[Presentation]
D[Domain]
DT[Data]
subgraph "Presentation Layer"
P --> Pages
P --> Controllers
P --> Bindings
P --> States
P --> Widgets
end
subgraph "Domain Layer"
D --> Entities
D --> Repositories
D --> UseCases
end
subgraph "Data Layer"
DT --> DataSources
DT --> Models
DT --> RepositoryImpl
DataSources --> LocalDS[Local DataSource]
DataSources --> RemoteDS[Remote DataSource]
end
end
mason make cleanforge_feature --feat feature_name
// This is entity class that is made as per the reponse of an api
class FeatureEntity {
final String id;
final String name;
// ... other properties
FeatureEntity({
required this.id,
required this.name,
});
}
// This is the parameter entity that you want to pass as /parameter/data/body while making api request
class FeatureRequestEntity {
final String id;
HomeRequestEntity({required this.id});
}
// This is the domain layer repository which is used to define functions that is called in use case
abstract class FeatureRepository {
ResultFuture<FeatureEntity> getFeature(String id);
ResultFuture<List<FeatureEntity>> getAllFeatures();
// ... other methods
}
// This is data source file of data layer, in this file we call the api
class FeatureRemoteDataSource {
final RestClient client;
Future<FeatureModel> getFeature(String id) async {
// Implementation
}
}
// This is the repository file(data layer) which implements repository file(domain layer).
// It basically connects data layer to domain layer
class FeatureRepositoryImpl implements FeatureRepository {
final FeatureRemoteDataSource remoteDataSource;
final FeatureLocalDataSource localDataSource;
@override
ResultFuture<FeatureEntity> getFeature({required FeatureRequestEntity request}) async {
// Implementation
}
}
// This is use case file which connect domain layer to presentation layer
// For every use case it is recommended to make seperate use case file
class GetFeatureUseCase implements UseCaseWithParams<FeatureEntity, GetFeatureDataUseCaseParams> {
final FeatureRepository repository;
@override
ResultFuture<FeatureEntity> call(String params) async {
return await repository.getFeature(params);
}
}
class GetFeatureDataUseCaseParams {
final FeatureRequestEntity request;
GetFeatureDataUseCaseParams({required this.request});
}
class FeatureController extends GetxController {
final GetFeatureUseCase getFeatureUseCase;
final featureState state;
Future<void> getFeature(String id) async {
final request = HomeRequestEntity(
id: "1",
);
final result =
await getFeatureUseCase(GetFeatureDataUseCaseParams(request: request));
result.fold(
(failure) {
Log.error(failure, ["error while fetching Home Data"]);
// ... other code
},
(data) => state.featureData.value = data,
)
}
}
class FeaturePage extends GetView<FeatureController> {
@override
Widget build(BuildContext context) {
return Scaffold(
// Implementation
);
}
}
When you generate a new feature, CleanForge automatically:
app_routes.dart
:
abstract class Routes {
static const HOME = '/home';
static const PROFILE = '/profile';
// New routes automatically added here
}
app_pages.dart
:
class AppPages {
static final routes = [
GetPage(
name: Routes.HOME,
page: () => const HomePage(),
binding: HomeBinding(),
middlewares: [AuthMiddleware()],
),
// New routes automatically configured
];
}
CleanForge generates and configures:
Example of auto-generated binding:
class FeatureBinding extends Bindings {
@override
void dependencies() {
// Data Sources
Get.lazyPut<FeatureRemoteDataSource>(
() => FeatureRemoteDataSourceImpl(client: Get.find()),
);
// Repositories
Get.lazyPut<FeatureRepository>(
() => FeatureRepositoryImpl(
remoteDataSource: Get.find(),
localDataSource: Get.find(),
),
);
// Use Cases
Get.lazyPut(() => GetFeatureUseCase(repository: Get.find()));
// Controllers
Get.lazyPut(() => FeatureController(
getFeatureUseCase: Get.find(),
));
}
}