createFeature function

Future<void> createFeature(
  1. String featureName, {
  2. String? stateArg,
})

Creates a new feature folder structure and optional state management boilerplate.

featureName - the feature folder name (e.g. "auth"). stateArg - optional: one of 'bloc','cubit','riverpod','provider','getx','none'.

Implementation

Future<void> createFeature(String featureName, {String? stateArg}) async {
  final green = AnsiPen()..green();
  final red = AnsiPen()..red();

  final projectRoot = _findProjectRoot();

  if (projectRoot == null) {
    print(red('āŒ Could not find pubspec.yaml — please run this inside a Flutter project.'));
    exit(1);
  }

  // Start spinner
  stdout.write('šŸš€ Creating feature "$featureName"... ');
  final spinner = ['|', '/', '-', '\\'];
  var i = 0;
  final timer = Timer.periodic(const Duration(milliseconds: 120), (_) {
    stdout.write('\ršŸš€ Creating feature "$featureName"... ${spinner[i++ % spinner.length]}');
  });

  try {
    // Determine state management (use provided arg if valid, otherwise load/save or ask)
    final stateManagement = _determineStateManagement(projectRoot, stateArg);

    final base = p.join(projectRoot.path, 'lib', 'features', featureName);

    final dirs = [
      'data/models',
      'data/datasources',
      'domain/entities',
      'domain/repositories',
      'domain/usecases',
      'presentation/screens',
      'presentation/widgets',
    ];

    // Add state-management specific folders
    switch (stateManagement) {
      case 'bloc':
      case 'cubit':
        dirs.add('presentation/bloc');
        break;
      case 'riverpod':
      case 'provider':
        dirs.add('presentation/providers');
        break;
      case 'getx':
        dirs.add('presentation/controllers');
        break;
      case 'none':
      default:
        break;
    }

    // Create directories
    for (var dir in dirs) {
      createDir(p.join(base, dir));
    }

    // Create common files
    createFile(p.join(base, 'data/models/${featureName}_model.dart'), '''
class ${_pascal(featureName)}Model {
  // TODO: Define model fields
}
''');

    createFile(p.join(base, 'domain/entities/${featureName}_entity.dart'), '''
class ${_pascal(featureName)}Entity {
  // TODO: Define entity fields
}
''');

    createFile(
      p.join(base, 'domain/repositories/${featureName}_repository.dart'),
      '''
abstract class ${_pascal(featureName)}Repository {
  // TODO: Define abstract methods
}
''',
    );

    createFile(
      p.join(base, 'presentation/screens/${featureName}_screen.dart'),
      '''
import 'package:flutter/material.dart';

class ${_pascal(featureName)}Screen extends StatelessWidget {
  const ${_pascal(featureName)}Screen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('${_pascal(featureName)} Screen')),
      body: const Center(child: Text('Welcome to ${_pascal(featureName)} feature!')),
    );
  }
}
''',
    );

    // Generate state management boilerplate (if any)
    _generateStateManagement(stateManagement, featureName, base);

    // Success
    stdout.write('\r'); // clear spinner line
    print(green('āœ… Feature "$featureName" with "$stateManagement" state management generated successfully!\n'));
  } finally {
    // Always cancel the spinner
    if (timer.isActive) timer.cancel();
  }
}