clix 1.2.0
clix: ^1.2.0 copied to clipboard
Powerful Dart CLI toolkit for building interactive command-line apps with prompts, colors, logging, and progress indicators.
Clix #
Why Clix? #
A modern, batteries-included CLI toolkit for Dart, focused on interactive workflows and developer experience.
β¨ All-in-One β Prompts, logging, progress, validation, styling, and more in a single package
π Production Ready β 537+ automated tests ensure rock-solid reliability
π― Type Safe β Full null safety with comprehensive type support
π§ͺ Test Friendly β Built-in mock I/O for effortless testing
β‘ Modern API β Clean, fluent .interact() syntax throughout
When Clix Is a Good Fit #
Clix is ideal for interactive CLI tools, setup wizards, and developer utilities.
For low-level argument parsing or simple one-off scripts, libraries like args may be more appropriate. Clix is designed to complement them, not replace them.
Quick Start #
dart pub add clix
Or add to your pubspec.yaml:
dependencies:
clix: ^1.2.0
import 'package:clix/clix.dart';
void main() async {
// Get user input with validation
final name = await Input('Your name').interact();
final confirmed = await Confirm('Continue?').interact();
// Beautiful logging
final logger = CliLogger.defaults();
logger.success('Welcome $name! π');
// Show progress
final spinner = Spinner('Setting things up...');
spinner.start();
await Future.delayed(Duration(seconds: 2));
spinner.complete('All done!');
}

Interactive Prompts #
Quick Examples #
// Text input with validation
final email = await Input(
'Email',
validator: (value) => value.contains('@') ? null : 'Invalid email',
).interact();
// Number input with range
final age = await Number('Age', min: 0, max: 120).interact();
// Choose from options
final framework = await Select('Framework', [
'Flutter', 'React', 'Vue', 'Angular'
]).interact();
// Multiple selection
final features = await MultiSelect('Features', [
'Auth', 'Database', 'API', 'Testing'
]).interact();
// Smart search through data
final country = await Search('Country', options: [
'United States', 'United Kingdom', 'Germany', 'France'
]).interact();
Unique Features #
Number & Decimal Prompts β Built-in range validation, the only CLI package offering these:
final price = await Decimal('Price', min: 0.0, max: 999.99).interact();
final quantity = await Number('Quantity', min: 1, max: 100).interact();
Smart Search β Interactive search with auto-completion:

final result = await Search(
'Find user',
options: (query) async => await searchAPI(query), // Dynamic search
minQueryLength: 2,
).interact();
Validation System #
Two approaches for maximum flexibility:
Modern: ValidationRules (Recommended) #
// Simple & clean - ValidationRules
final username = await Input(
'Username',
validator: ValidationRules()
.required()
.min(3)
.alphaNumeric(),
).interact();
// Advanced with custom messages - Validator.rules
final password = await Input(
'Password',
validator: Validator.rules([
(value) => Validator.required(value, 'Password is required'),
(value) => Validator.min(value, 8, 'At least 8 characters'),
(value) => Validator.pattern(value, RegExp(r'[A-Z]'), 'Must contain uppercase'),
(value) => value.contains('password') ? 'Cannot contain "password"' : null,
]),
).interact();
Two modern approaches: ValidationRules fluent API and Validator.rules array-based chains.
Available Rules: required(), min(), max(), email(), url(), numeric(), alpha(), alphaNumeric(), pattern(), custom(), and more.
Custom: Validator Functions #
final username = await Input(
'Username',
validator: (value) {
if (value.length < 3) return 'Too short';
if (value.length > 20) return 'Too long';
if (RegExp(r'[^a-zA-Z0-9]').hasMatch(value)) return 'Only letters and numbers';
if (['admin', 'root'].contains(value.toLowerCase())) return 'Reserved name';
return null; // Valid
},
).interact();
Logging & Output #
Simple & Effective #
final logger = CliLogger();
// Basic levels
logger.info('App started');
logger.success('Task completed');
logger.warn('Warning message');
logger.error('Something failed');
// With helpful hints
logger.successWithHint('Build completed', hint: 'Run: dart test');
logger.errorWithHint('Connection failed', hint: 'Check network');

Colors & Styling #
// Colors
logger.red('Error text');
logger.green('Success text');
logger.blue('Info text');
// Custom styling
final style = CliStyle()
.withColor(CliColor.cyan)
.makeBold()
.makeUnderline();
print(style.format('Styled text'));
Custom Themes #
// Create custom theme with hex colors
final customTheme = CliTheme(
primary: CliStyle().withColor(CliColor.hex('#FF6B6B')), // Coral Red
secondary: CliStyle().withColor(CliColor.hex('#4ECDC4')), // Teal
success: CliStyle().withColor(CliColor.hex('#45B7D1')), // Sky Blue
warn: CliStyle().withColor(CliColor.hex('#FFA726')), // Orange
error: CliStyle().withColor(CliColor.hex('#E74C3C')), // Red
);
final customLogger = CliLogger(theme: customTheme);
customLogger.primary('Custom branded colors');
customLogger.success('Success with custom theme');
Additional Logger Features #
// Background colors
logger.onSuccess('Success background');
logger.onError('Error background');
logger.onWarning('Warning background');
// Icons & formatting
logger.successIcon('Done!');
logger.errorIcon('Failed');
logger.tree('Project structure');
logger.point('Bullet point');
logger.newLine(); // Spacing
Progress & Feedback #
Spinners #
final spinner = Spinner('Loading...');
// Update message
spinner.update('Processing...');
await Future.delayed(Duration(seconds: 2));
// Finish
spinner.complete('Success!');
// or spinner.fail('Failed');

Spinner Types: dots, line, pipe, clock, arrow, triangle, square, circle
Progress Bars & Multi-Spinners #
// Progress bar
final progress = logger.progress(total: 100);
for (int i = 0; i <= 100; i += 10) {
progress.update(i, 'Step $i/100');
await Future.delayed(Duration(milliseconds: 100));
}
progress.complete();
// Multiple concurrent tasks
final multiSpinner = logger.multiSpinner();
// Add tasks
multiSpinner.add('packages', 'Downloading packages');
multiSpinner.add('env', 'Setting up environment');
// Start tasks
multiSpinner.startTask('packages');
multiSpinner.startTask('env');
// Complete first task
await Future.delayed(Duration(milliseconds: 800));
multiSpinner.complete('packages', 'Packages downloaded');
// Update second task message
await Future.delayed(Duration(milliseconds: 600));
multiSpinner.updateMessage('env', 'Configuring environment');
// Complete second task
await Future.delayed(Duration(milliseconds: 400));
multiSpinner.complete('env', 'Environment ready');
// Stop all tasks
await Future.delayed(Duration(milliseconds: 200));
multiSpinner.stop();
Data Display #
Tables #
logger.table(
columns: [
TableColumn('Name'),
TableColumn('Age', alignment: TableAlignment.center),
TableColumn('City', alignment: TableAlignment.right),
],
rows: [
['Alice', '25', 'New York'],
['Bob', '30', 'London'],
['Charlie', '35', 'Tokyo'],
],
);
Configuration Management #
// JSON configuration
final config = CliConfig.fromJson('config.json');
await config.load();
final apiUrl = config.getValue<String>('api.url');
final timeout = config.getValue<int>('api.timeout', defaultValue: 30);
// Validation
final errors = config.validate();
if (errors.isNotEmpty) {
logger.error('Config errors: ${errors.join(', ')}');
}
Testing Made Easy #
Clix includes built-in mock I/O for seamless testing:
import 'package:test/test.dart';
import 'package:clix/clix.dart';
test('user interaction', () async {
final mockIO = MockIO();
final theme = CliTheme.defaultTheme();
// Setup responses
mockIO.setInput(['John', 'y']);
// Test prompts
final name = await Input('Name').interact(mockIO, theme);
final confirmed = await Confirm('Continue?').interact(mockIO, theme);
expect(name, equals('John'));
expect(confirmed, isTrue);
});
Complete Example #
import 'package:clix/clix.dart';
Future<void> main() async {
final logger = CliLogger();
logger.primary('Project Setup Wizard');
logger.newLine();
try {
// Gather project info
final name = await Input(
'Project name',
validator: ValidationRules()
.required('Project name is required')
.pattern(RegExp(r'^[a-z][a-z0-9_]*$'), 'Invalid format'),
).interact();
final framework = await Select('Framework', [
'Flutter', 'Dart Console', 'Web App'
]).interact();
final features = await MultiSelect('Features', [
'Authentication', 'Database', 'API', 'Testing'
]).interact();
// Confirm setup
final proceed = await Confirm('Create project?').interact();
if (proceed) {
// Show progress
final spinner = Spinner('Creating project...');
await Future.delayed(Duration(seconds: 2));
spinner.update('Installing dependencies...');
await Future.delayed(Duration(seconds: 1));
spinner.complete('Project created! π');
// Results
logger.successWithHint(
'Project: $name',
hint: 'cd $name',
);
logger.infoWithHint(
'Framework: $framework',
hint: 'dart run',
);
logger.primary('Features: ${features.join(', ')}');
}
} catch (e) {
logger.error('Setup failed: $e');
}
}
Key Features #
| Feature | Description |
|---|---|
| π― Interactive Prompts | Input, Select, MultiSelect, Confirm, Number, Decimal, Search, Password |
| β Smart Validation | ValidationRules fluent API + traditional validator functions |
| π¨ Rich Styling | Colors, themes, backgrounds, icons, and custom formatting |
| π Advanced Logging | Multi-level logging with hints, icons, and timestamps |
| π¬ Progress Indicators | Spinners, progress bars, multi-spinners with customization |
| π Data Display | Tables, structured output, and configuration management |
| π§ͺ Testing Support | Built-in MockIO for comprehensive testing |
| π‘οΈ Production Ready | 537+ automated tests, null safety, comprehensive error handling |
Examples & Resources #
# Try the examples
dart run example/basic_prompts.dart
dart run example/config_example.dart
dart run example/validation_demo.dart
dart run example/complete_demo.dart
- π Full API Documentation
- π Report Issues
- π¦ Pub Package
Built with β€οΈ for the Dart community
β Star on GitHub β’ π Documentation β’ π¬ Discussions
