run method
Runs this command.
The return value is wrapped in a Future if necessary and returned by
CommandRunner.runCommand.
Implementation
@override
Future<void> run() async {
// 1. Get the feature name
String? featureNameArg = argResults?.rest.isNotEmpty == true ? argResults!.rest.first : null;
if (featureNameArg == null || featureNameArg.trim().isEmpty) {
throw CliException('Please specify a feature name. Usage: flutter_init_cli generate <feature_name>');
}
final featureName = featureNameArg.trim();
final snakeName = CaseHelper.toSnakeCase(featureName);
final pascalName = CaseHelper.toPascalCase(featureName);
final camelName = CaseHelper.toCamelCase(featureName);
final upperName = CaseHelper.toUpperCase(featureName);
// 2. Load config file from current directory
final projectPath = Directory.current.path;
final configFile = File(p.join(projectPath, '.flutter_init_cli.json'));
if (!configFile.existsSync()) {
throw CliException(
'Could not find .flutter_init_cli.json in the current directory.',
'Ensure you run this command inside the root folder of a project generated by flutter_init_cli.',
);
}
Map<String, dynamic> config;
try {
config = jsonDecode(configFile.readAsStringSync()) as Map<String, dynamic>;
} catch (e) {
throw CliException('Failed to parse .flutter_init_cli.json config file.', e.toString());
}
final projectName = config['projectName'] as String? ?? 'app';
final architecture = config['architecture'] as String? ?? 'featureFirst';
final stateManagement = config['stateManagement'] as String? ?? 'provider';
final routing = config['routing'] as String? ?? 'navigator';
Logger.header('GENERATING FEATURE: $pascalName');
final isFeatureBased = architecture == 'featureFirst' || architecture == 'cleanArchitecture';
final stateFolder = _getStateManagementFolder(stateManagement, architecture);
// 3. Resolve target directories
String targetViewDir;
String targetStateDir;
if (isFeatureBased) {
final featureDir = p.join(projectPath, 'lib', 'features', snakeName);
// Presentation directories
targetViewDir = p.join(featureDir, 'presentation', 'views');
targetStateDir = p.join(featureDir, 'presentation', stateFolder);
final targetWidgetDir = p.join(featureDir, 'presentation', 'widgets');
// Data directories
final targetDataDir = p.join(featureDir, 'data');
final targetDataSourcesDir = p.join(targetDataDir, 'datasources');
final targetModelsDir = p.join(targetDataDir, 'models');
final targetDataRepositoriesDir = p.join(targetDataDir, 'repositories');
// Domain directories
final targetDomainDir = p.join(featureDir, 'domain');
final targetEntitiesDir = p.join(targetDomainDir, 'entities');
final targetDomainRepositoriesDir = p.join(targetDomainDir, 'repositories');
final targetUseCasesDir = p.join(targetDomainDir, 'usecases');
// Create all directories for Feature-First/Clean Architecture
Services.fileService.createDir(targetViewDir);
Services.fileService.createDir(targetStateDir);
Services.fileService.createDir(targetWidgetDir);
Services.fileService.createDir(targetDataDir);
Services.fileService.createDir(targetDataSourcesDir);
Services.fileService.createDir(targetModelsDir);
Services.fileService.createDir(targetDataRepositoriesDir);
Services.fileService.createDir(targetDomainDir);
Services.fileService.createDir(targetEntitiesDir);
Services.fileService.createDir(targetDomainRepositoriesDir);
Services.fileService.createDir(targetUseCasesDir);
} else {
targetViewDir = p.join(projectPath, 'lib', 'views');
targetStateDir = p.join(projectPath, 'lib', stateFolder);
// Ensure directories exist for other architectures
Services.fileService.createDir(targetViewDir);
Services.fileService.createDir(targetStateDir);
}
// 4. Set up replacements map
final replacements = {
'home_provider': '${snakeName}_provider',
'home_bloc': '${snakeName}_bloc',
'home_event': '${snakeName}_event',
'home_state': '${snakeName}_state',
'home_controller': '${snakeName}_controller',
'home_store': '${snakeName}_store',
'Home': pascalName,
'home': camelName,
'HOME': upperName,
'{{PROJECT_NAME}}': projectName,
};
// 5. Generate template files
await _generateFeatureFiles(
stateManagement: stateManagement,
targetViewDir: targetViewDir,
targetStateDir: targetStateDir,
snakeName: snakeName,
replacements: replacements,
stateFolder: stateFolder,
);
// 6. Integrate into Routing System
_integrateRouting(
projectPath: projectPath,
snakeName: snakeName,
pascalName: pascalName,
camelName: camelName,
isFeatureBased: isFeatureBased,
routing: routing,
);
Logger.boldInfo('\nSuccessfully generated "$pascalName" feature!');
}