Outil CLI Metro
Introduction
Metro est un outil CLI qui fonctionne sous le capot du framework Nylo Website. Il fournit de nombreux outils utiles pour accelerer le developpement.
Installation
Lorsque vous creez un nouveau projet Nylo avec nylo init, la commande metro est automatiquement configuree pour votre terminal. Vous pouvez l'utiliser immediatement dans n'importe quel projet Nylo.
Executez metro depuis le repertoire de votre projet pour voir toutes les commandes disponibles :
metro
Vous devriez voir une sortie similaire a celle ci-dessous.
Metro - Nylo's Companion to Build Flutter apps by Anthony Gordon
Usage:
command [options] [arguments]
Options
-h
All commands:
[Widget Commands]
make:page
make:stateful_widget
make:stateless_widget
make:state_managed_widget
make:navigation_hub
make:journey_widget
make:bottom_sheet_modal
make:button
make:form
[Helper Commands]
make:model
make:provider
make:api_service
make:controller
make:event
make:theme
make:route_guard
make:config
make:interceptor
make:command
make:env
make:key
Creer un controleur
Creer un nouveau controleur
Vous pouvez creer un nouveau controleur en executant la commande suivante dans le terminal.
metro make:controller profile_controller
Cela creera un nouveau controleur s'il n'existe pas deja dans le repertoire lib/app/controllers/.
Forcer la creation d'un controleur
Arguments :
L'utilisation du flag --force ou -f ecrasera un controleur existant s'il existe deja.
metro make:controller profile_controller --force
Creer un modele
Creer un nouveau modele
Vous pouvez creer un nouveau modele en executant la commande suivante dans le terminal.
metro make:model product
Le modele nouvellement cree sera place dans lib/app/models/.
Creer un modele a partir de JSON
Arguments :
L'utilisation du flag --json ou -j creera un nouveau modele a partir d'un payload JSON.
metro make:model product --json
Ensuite, vous pouvez coller votre JSON dans le terminal et il generera un modele pour vous.
Forcer la creation d'un modele
Arguments :
L'utilisation du flag --force ou -f ecrasera un modele existant s'il existe deja.
metro make:model product --force
Creer une page
- Creer une nouvelle page
- Creer une page avec un controleur
- Creer une page d'authentification
- Creer une page initiale
- Forcer la creation d'une page
Creer une nouvelle page
Vous pouvez creer une nouvelle page en executant la commande suivante dans le terminal.
metro make:page product_page
Cela creera une nouvelle page si elle n'existe pas deja dans le repertoire lib/resources/pages/.
Creer une page avec un controleur
Vous pouvez creer une nouvelle page avec un controleur en executant la commande suivante dans le terminal.
Arguments :
L'utilisation du flag --controller ou -c creera une nouvelle page avec un controleur.
metro make:page product_page -c
Creer une page d'authentification
Vous pouvez creer une nouvelle page d'authentification en executant la commande suivante dans le terminal.
Arguments :
L'utilisation du flag --auth ou -a creera une nouvelle page d'authentification.
metro make:page login_page -a
Creer une page initiale
Vous pouvez creer une nouvelle page initiale en executant la commande suivante dans le terminal.
Arguments :
L'utilisation du flag --initial ou -i creera une nouvelle page initiale.
metro make:page home_page -i
Forcer la creation d'une page
Arguments :
L'utilisation du flag --force ou -f ecrasera une page existante si elle existe deja.
metro make:page product_page --force
Creer un widget stateless
Creer un nouveau widget stateless
Vous pouvez creer un nouveau widget stateless en executant la commande suivante dans le terminal.
metro make:stateless_widget product_rating_widget
La commande ci-dessus creera un nouveau widget s'il n'existe pas deja dans le repertoire lib/resources/widgets/.
Forcer la creation d'un widget stateless
Arguments :
L'utilisation du flag --force ou -f ecrasera un widget existant s'il existe deja.
metro make:stateless_widget product_rating_widget --force
Creer un widget stateful
Creer un nouveau widget stateful
Vous pouvez creer un nouveau widget stateful en executant la commande suivante dans le terminal.
metro make:stateful_widget product_rating_widget
La commande ci-dessus creera un nouveau widget s'il n'existe pas deja dans le repertoire lib/resources/widgets/.
Forcer la creation d'un widget stateful
Arguments :
L'utilisation du flag --force ou -f ecrasera un widget existant s'il existe deja.
metro make:stateful_widget product_rating_widget --force
Creer un widget journey
Creer un nouveau widget journey
Vous pouvez creer un nouveau widget journey en executant la commande suivante dans le terminal.
metro make:journey_widget product_journey --parent="[NAVIGATION_HUB]"
# Full example if you have a BaseNavigationHub
metro make:journey_widget welcome,user_dob,user_photos --parent="Base"
La commande ci-dessus creera un nouveau widget s'il n'existe pas deja dans le repertoire lib/resources/widgets/.
L'argument --parent est utilise pour specifier le widget parent auquel le nouveau widget journey sera ajoute.
Exemple
metro make:navigation_hub onboarding
Ensuite, ajoutez les nouveaux widgets journey.
metro make:journey_widget welcome,user_dob,user_photos --parent="onboarding"
Forcer la creation d'un widget journey
Arguments :
L'utilisation du flag --force ou -f ecrasera un widget existant s'il existe deja.
metro make:journey_widget product_journey --force --parent="[YOUR_NAVIGATION_HUB]"
Creer un service API
- Creer un nouveau service API
- Creer un nouveau service API avec un modele
- Forcer la creation d'un service API
Creer un nouveau service API
Vous pouvez creer un nouveau service API en executant la commande suivante dans le terminal.
metro make:api_service user_api_service
Le service API nouvellement cree sera place dans lib/app/networking/.
Creer un nouveau service API avec un modele
Vous pouvez creer un nouveau service API avec un modele en executant la commande suivante dans le terminal.
Arguments :
L'utilisation de l'option --model ou -m creera un nouveau service API avec un modele.
metro make:api_service user --model="User"
Le service API nouvellement cree sera place dans lib/app/networking/.
Forcer la creation d'un service API
Arguments :
L'utilisation du flag --force ou -f ecrasera un service API existant s'il existe deja.
metro make:api_service user --force
Creer un evenement
Creer un nouvel evenement
Vous pouvez creer un nouvel evenement en executant la commande suivante dans le terminal.
metro make:event login_event
Cela creera un nouvel evenement dans lib/app/events.
Forcer la creation d'un evenement
Arguments :
L'utilisation du flag --force ou -f ecrasera un evenement existant s'il existe deja.
metro make:event login_event --force
Creer un provider
Creer un nouveau provider
Creez de nouveaux providers dans votre application en utilisant la commande ci-dessous.
metro make:provider firebase_provider
Le provider nouvellement cree sera place dans lib/app/providers/.
Forcer la creation d'un provider
Arguments :
L'utilisation du flag --force ou -f ecrasera un provider existant s'il existe deja.
metro make:provider firebase_provider --force
Creer un theme
Creer un nouveau theme
Vous pouvez creer des themes en executant la commande suivante dans le terminal.
metro make:theme bright_theme
Cela creera un nouveau theme dans lib/resources/themes/.
Forcer la creation d'un theme
Arguments :
L'utilisation du flag --force ou -f ecrasera un theme existant s'il existe deja.
metro make:theme bright_theme --force
Creer des formulaires
Creer un nouveau formulaire
Vous pouvez creer un nouveau formulaire en executant la commande suivante dans le terminal.
metro make:form car_advert_form
Cela creera un nouveau formulaire dans lib/app/forms.
Forcer la creation d'un formulaire
Arguments :
L'utilisation du flag --force ou -f ecrasera un formulaire existant s'il existe deja.
metro make:form login_form --force
Creer un garde de route
Creer un nouveau garde de route
Vous pouvez creer un garde de route en executant la commande suivante dans le terminal.
metro make:route_guard premium_content
Cela creera un nouveau garde de route dans lib/app/route_guards.
Forcer la creation d'un garde de route
Arguments :
L'utilisation du flag --force ou -f ecrasera un garde de route existant s'il existe deja.
metro make:route_guard premium_content --force
Creer un fichier de configuration
Creer un nouveau fichier de configuration
Vous pouvez creer un nouveau fichier de configuration en executant la commande suivante dans le terminal.
metro make:config shopping_settings
Cela creera un nouveau fichier de configuration dans lib/app/config.
Forcer la creation d'un fichier de configuration
Arguments :
L'utilisation du flag --force ou -f ecrasera un fichier de configuration existant s'il existe deja.
metro make:config app_config --force
Creer une commande
Creer une nouvelle commande
Vous pouvez creer une nouvelle commande en executant la commande suivante dans le terminal.
metro make:command my_command
Cela creera une nouvelle commande dans lib/app/commands.
Forcer la creation d'une commande
Arguments :
L'utilisation du flag --force ou -f ecrasera une commande existante si elle existe deja.
metro make:command my_command --force
Creer un widget a etat gere
Vous pouvez creer un nouveau widget a etat gere en executant la commande suivante dans le terminal.
metro make:state_managed_widget product_rating_widget
La commande ci-dessus creera un nouveau widget dans lib/resources/widgets/.
L'utilisation du flag --force ou -f ecrasera un widget existant s'il existe deja.
metro make:state_managed_widget product_rating_widget --force
Creer un Navigation Hub
Vous pouvez creer un nouveau navigation hub en executant la commande suivante dans le terminal.
metro make:navigation_hub dashboard
Cela creera un nouveau navigation hub dans lib/resources/pages/ et ajoutera automatiquement la route.
Arguments :
| Flag | Court | Description |
|---|---|---|
--auth |
-a |
Creer comme page d'authentification |
--initial |
-i |
Creer comme page initiale |
--force |
-f |
Ecraser si existant |
# Create as the initial page
metro make:navigation_hub dashboard --initial
Creer un modal de bas de page
Vous pouvez creer un nouveau modal de bas de page en executant la commande suivante dans le terminal.
metro make:bottom_sheet_modal payment_options
Cela creera un nouveau modal de bas de page dans lib/resources/widgets/.
L'utilisation du flag --force ou -f ecrasera un modal existant s'il existe deja.
metro make:bottom_sheet_modal payment_options --force
Creer un bouton
Vous pouvez creer un nouveau widget bouton en executant la commande suivante dans le terminal.
metro make:button checkout_button
Cela creera un nouveau widget bouton dans lib/resources/widgets/.
L'utilisation du flag --force ou -f ecrasera un bouton existant s'il existe deja.
metro make:button checkout_button --force
Creer un intercepteur
Vous pouvez creer un nouvel intercepteur reseau en executant la commande suivante dans le terminal.
metro make:interceptor auth_interceptor
Cela creera un nouvel intercepteur dans lib/app/networking/dio/interceptors/.
L'utilisation du flag --force ou -f ecrasera un intercepteur existant s'il existe deja.
metro make:interceptor auth_interceptor --force
Creer un fichier Env
Vous pouvez creer un nouveau fichier d'environnement en executant la commande suivante dans le terminal.
metro make:env .env.staging
Cela creera un nouveau fichier .env a la racine de votre projet.
Generer une cle
Generez un APP_KEY securise pour le chiffrement de l'environnement. Celui-ci est utilise pour les fichiers .env chiffres dans la v7.
metro make:key
Arguments :
| Flag / Option | Court | Description |
|---|---|---|
--force |
-f |
Ecraser l'APP_KEY existant |
--file |
-e |
Fichier .env cible (par defaut : .env) |
# Generate key and overwrite existing
metro make:key --force
# Generate key for a specific env file
metro make:key --file=.env.production
Generer les icones d'application
Vous pouvez generer toutes les icones d'application pour iOS et Android en executant la commande ci-dessous.
dart run flutter_launcher_icons:main
Cela utilise la configuration flutter_icons dans votre fichier pubspec.yaml.
Commandes personnalisees
Les commandes personnalisees vous permettent d'etendre la CLI de Nylo avec vos propres commandes specifiques au projet. Cette fonctionnalite vous permet d'automatiser les taches repetitives, d'implementer des workflows de deploiement ou d'ajouter toute fonctionnalite personnalisee directement dans les outils en ligne de commande de votre projet.
- Creer des commandes personnalisees
- Executer des commandes personnalisees
- Ajouter des options aux commandes
- Ajouter des flags aux commandes
- Methodes d'aide
Note : Vous ne pouvez actuellement pas importer nylo_framework.dart dans vos commandes personnalisees, veuillez utiliser ny_cli.dart a la place.
Creer des commandes personnalisees
Pour creer une nouvelle commande personnalisee, vous pouvez utiliser la fonctionnalite make:command :
metro make:command current_time
Vous pouvez specifier une categorie pour votre commande en utilisant l'option --category :
# Specify a category
metro make:command current_time --category="project"
Cela creera un nouveau fichier de commande dans lib/app/commands/current_time.dart avec la structure suivante :
import 'package:nylo_framework/metro/ny_cli.dart';
void main(arguments) => _CurrentTimeCommand(arguments).run();
/// Current Time Command
///
/// Usage:
/// metro app:current_time
class _CurrentTimeCommand extends NyCustomCommand {
_CurrentTimeCommand(super.arguments);
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption('format', defaultValue: 'HH:mm:ss');
return command;
}
@override
Future<void> handle(CommandResult result) async {
final format = result.getString("format");
// Get the current time
final now = DateTime.now();
final DateFormat dateFormat = DateFormat(format);
// Format the current time
final formattedTime = dateFormat.format(now);
info("The current time is " + formattedTime);
}
}
La commande sera automatiquement enregistree dans le fichier lib/app/commands/commands.json, qui contient une liste de toutes les commandes enregistrees :
[
{
"name": "install_firebase",
"category": "project",
"script": "install_firebase.dart"
},
{
"name": "current_time",
"category": "app",
"script": "current_time.dart"
}
]
Executer des commandes personnalisees
Une fois creee, vous pouvez executer votre commande personnalisee en utilisant soit le raccourci Metro, soit la commande Dart complete :
metro app:current_time
Lorsque vous executez metro sans arguments, vous verrez vos commandes personnalisees listees dans le menu sous la section "Custom Commands" :
[Custom Commands]
app:app_icon
app:clear_pub
project:install_firebase
project:deploy
Pour afficher les informations d'aide pour votre commande, utilisez le flag --help ou -h :
metro project:install_firebase --help
Ajouter des options aux commandes
Les options permettent a votre commande d'accepter des entrees supplementaires des utilisateurs. Vous pouvez ajouter des options a votre commande dans la methode builder :
@override
CommandBuilder builder(CommandBuilder command) {
// Add an option with a default value
command.addOption(
'environment', // option name
abbr: 'e', // short form abbreviation
help: 'Target deployment environment', // help text
defaultValue: 'development', // default value
allowed: ['development', 'staging', 'production'] // allowed values
);
return command;
}
Ensuite, accedez a la valeur de l'option dans la methode handle de votre commande :
@override
Future<void> handle(CommandResult result) async {
final environment = result.getString('environment');
info('Deploying to $environment environment...');
// Command implementation...
}
Exemple d'utilisation :
metro project:deploy --environment=production
# or using abbreviation
metro project:deploy -e production
Ajouter des flags aux commandes
Les flags sont des options booleennes qui peuvent etre activees ou desactivees. Ajoutez des flags a votre commande en utilisant la methode addFlag :
@override
CommandBuilder builder(CommandBuilder command) {
command.addFlag(
'verbose', // flag name
abbr: 'v', // short form abbreviation
help: 'Enable verbose output', // help text
defaultValue: false // default to off
);
return command;
}
Ensuite, verifiez l'etat du flag dans la methode handle de votre commande :
@override
Future<void> handle(CommandResult result) async {
final verbose = result.getBool('verbose');
if (verbose) {
info('Verbose mode enabled');
// Additional logging...
}
// Command implementation...
}
Exemple d'utilisation :
metro project:deploy --verbose
# or using abbreviation
metro project:deploy -v
Methodes d'aide
La classe de base NyCustomCommand fournit plusieurs methodes d'aide pour les taches courantes :
Affichage de messages
Voici quelques methodes pour afficher des messages dans differentes couleurs :
info |
Afficher un message d'information en texte bleu |
error |
Afficher un message d'erreur en texte rouge |
success |
Afficher un message de succes en texte vert |
warning |
Afficher un message d'avertissement en texte jaune |
Execution de processus
Executer des processus et afficher leur sortie dans la console :
addPackage |
Ajouter un package a pubspec.yaml |
addPackages |
Ajouter plusieurs packages a pubspec.yaml |
runProcess |
Executer un processus externe et afficher la sortie dans la console |
prompt |
Collecter la saisie utilisateur sous forme de texte |
confirm |
Poser une question oui/non et retourner un resultat booleen |
select |
Presenter une liste d'options et laisser l'utilisateur en choisir une |
multiSelect |
Permettre a l'utilisateur de selectionner plusieurs options dans une liste |
Requetes reseau
Effectuer des requetes reseau via la console :
api |
Effectuer un appel API en utilisant le client API Nylo |
Indicateur de chargement
Afficher un indicateur de chargement pendant l'execution d'une fonction :
withSpinner |
Afficher un indicateur de chargement pendant l'execution d'une fonction |
createSpinner |
Creer une instance de spinner pour un controle manuel |
Helpers pour commandes personnalisees
Vous pouvez egalement utiliser les methodes d'aide suivantes pour gerer les arguments de commande :
getString |
Obtenir une valeur string des arguments de commande |
getBool |
Obtenir une valeur booleenne des arguments de commande |
getInt |
Obtenir une valeur entiere des arguments de commande |
sleep |
Mettre en pause l'execution pour une duree specifiee |
Execution de processus externes
// Run a process with output displayed in the console
await runProcess('flutter build web --release');
// Run a process silently
await runProcess('flutter pub get', silent: true);
// Run a process in a specific directory
await runProcess('git pull', workingDirectory: './my-project');
Gestion des packages
// Add a package to pubspec.yaml
addPackage('firebase_core', version: '^2.4.0');
// Add a dev package to pubspec.yaml
addPackage('build_runner', dev: true);
// Add multiple packages at once
addPackages(['firebase_auth', 'firebase_storage', 'quickalert']);
Formatage de sortie
// Print status messages with color coding
info('Processing files...'); // Blue text
error('Operation failed'); // Red text
success('Deployment complete'); // Green text
warning('Outdated package'); // Yellow text
Methodes de saisie interactive
La classe de base NyCustomCommand fournit plusieurs methodes pour collecter les saisies utilisateur dans le terminal. Ces methodes facilitent la creation d'interfaces en ligne de commande interactives pour vos commandes personnalisees.
Saisie de texte
String prompt(String question, {String defaultValue = ''})
Affiche une question a l'utilisateur et collecte sa reponse textuelle.
Parametres :
question: La question ou l'invite a afficherdefaultValue: Valeur par defaut optionnelle si l'utilisateur appuie simplement sur Entree
Retourne : La saisie de l'utilisateur sous forme de string, ou la valeur par defaut si aucune saisie n'a ete fournie
Exemple :
final name = prompt('What is your project name?', defaultValue: 'my_app');
final description = prompt('Enter a project description:');
Confirmation
bool confirm(String question, {bool defaultValue = false})
Pose une question oui/non a l'utilisateur et retourne un resultat booleen.
Parametres :
question: La question oui/non a poserdefaultValue: La reponse par defaut (true pour oui, false pour non)
Retourne : true si l'utilisateur a repondu oui, false s'il a repondu non
Exemple :
if (confirm('Would you like to continue?', defaultValue: true)) {
// User confirmed or pressed Enter (accepting the default)
await runProcess('flutter pub get');
} else {
// User declined
info('Operation canceled');
}
Selection unique
String select(String question, List<String> options, {String? defaultOption})
Presente une liste d'options et laisse l'utilisateur en choisir une.
Parametres :
question: L'invite de selectionoptions: Liste des options disponiblesdefaultOption: Selection par defaut optionnelle
Retourne : L'option selectionnee sous forme de string
Exemple :
final environment = select(
'Select deployment environment:',
['development', 'staging', 'production'],
defaultOption: 'development'
);
info('Deploying to $environment environment...');
Selection multiple
List<String> multiSelect(String question, List<String> options)
Permet a l'utilisateur de selectionner plusieurs options dans une liste.
Parametres :
question: L'invite de selectionoptions: Liste des options disponibles
Retourne : Une liste des options selectionnees
Exemple :
final packages = multiSelect(
'Select packages to install:',
['firebase_auth', 'dio', 'provider', 'shared_preferences', 'path_provider']
);
if (packages.isNotEmpty) {
info('Installing ${packages.length} packages...');
addPackages(packages);
await runProcess('flutter pub get');
}
Methode d'aide API
La methode d'aide api simplifie les requetes reseau depuis vos commandes personnalisees.
Future<T?> api<T>(Future<T?> Function(ApiService) request) async
Exemples d'utilisation de base
Requete GET
// Fetch data
final userData = await api((request) =>
request.get('https://api.example.com/users/1')
);
Requete POST
// Create a resource
final result = await api((request) =>
request.post(
'https://api.example.com/items',
data: {'name': 'New Item', 'price': 19.99}
)
);
Requete PUT
// Update a resource
final updateResult = await api((request) =>
request.put(
'https://api.example.com/items/42',
data: {'name': 'Updated Item', 'price': 29.99}
)
);
Requete DELETE
// Delete a resource
final deleteResult = await api((request) => request.delete('https://api.example.com/items/42'));
Requete PATCH
// Partially update a resource
final patchResult = await api((request) => request.patch(
'https://api.example.com/items/42',
data: {'price': 24.99}
)
);
Avec des parametres de requete
// Add query parameters
final searchResults = await api((request) => request.get(
'https://api.example.com/search',
queryParameters: {'q': 'keyword', 'limit': 10}
)
);
Avec Spinner
// Using with spinner for better UI
final data = await withSpinner(
task: () async {
final data = await api((request) => request.get('https://api.example.com/config'));
// Process the data
},
message: 'Loading configuration',
);
Fonctionnalite Spinner
Les spinners fournissent un retour visuel pendant les operations longues dans vos commandes personnalisees. Ils affichent un indicateur anime accompagne d'un message pendant que votre commande execute des taches asynchrones, ameliorant l'experience utilisateur en montrant la progression et le statut.
Utiliser avec spinner
La methode withSpinner vous permet d'envelopper une tache asynchrone avec une animation de spinner qui demarre automatiquement lorsque la tache commence et s'arrete lorsqu'elle se termine ou echoue :
Future<T> withSpinner<T>({
required Future<T> Function() task,
required String message,
String? successMessage,
String? errorMessage,
}) async
Parametres :
task: La fonction asynchrone a executermessage: Texte a afficher pendant que le spinner tournesuccessMessage: Message optionnel a afficher en cas de succeserrorMessage: Message optionnel a afficher en cas d'echec
Retourne : Le resultat de la fonction de tache
Exemple :
@override
Future<void> handle(CommandResult result) async {
// Run a task with a spinner
final projectFiles = await withSpinner(
task: () async {
// Long-running task (e.g., analyzing project files)
await sleep(2);
return ['pubspec.yaml', 'lib/main.dart', 'README.md'];
},
message: 'Analyzing project structure',
successMessage: 'Project analysis complete',
errorMessage: 'Failed to analyze project',
);
// Continue with the results
info('Found ${projectFiles.length} key files');
}
Controle manuel du spinner
Pour des scenarios plus complexes ou vous devez controler manuellement l'etat du spinner, vous pouvez creer une instance de spinner :
ConsoleSpinner createSpinner(String message)
Parametres :
message: Texte a afficher pendant que le spinner tourne
Retourne : Une instance ConsoleSpinner que vous pouvez controler manuellement
Exemple avec controle manuel :
@override
Future<void> handle(CommandResult result) async {
// Create a spinner instance
final spinner = createSpinner('Deploying to production');
spinner.start();
try {
// First task
await runProcess('flutter clean', silent: true);
spinner.update('Building release version');
// Second task
await runProcess('flutter build web --release', silent: true);
spinner.update('Uploading to server');
// Third task
await runProcess('./deploy.sh', silent: true);
// Complete successfully
spinner.stop(completionMessage: 'Deployment completed successfully', success: true);
} catch (e) {
// Handle failure
spinner.stop(completionMessage: 'Deployment failed: $e', success: false);
rethrow;
}
}
Exemples
Tache simple avec Spinner
@override
Future<void> handle(CommandResult result) async {
await withSpinner(
task: () async {
// Install dependencies
await runProcess('flutter pub get', silent: true);
return true;
},
message: 'Installing dependencies',
successMessage: 'Dependencies installed successfully',
);
}
Plusieurs operations consecutives
@override
Future<void> handle(CommandResult result) async {
// First operation with spinner
await withSpinner(
task: () => runProcess('flutter clean', silent: true),
message: 'Cleaning project',
);
// Second operation with spinner
await withSpinner(
task: () => runProcess('flutter pub get', silent: true),
message: 'Updating dependencies',
);
// Third operation with spinner
final buildSuccess = await withSpinner(
task: () async {
await runProcess('flutter build apk --release', silent: true);
return true;
},
message: 'Building release APK',
successMessage: 'Release APK built successfully',
);
if (buildSuccess) {
success('Build process completed');
}
}
Workflow complexe avec controle manuel
@override
Future<void> handle(CommandResult result) async {
final spinner = createSpinner('Starting deployment process');
spinner.start();
try {
// Run multiple steps with status updates
spinner.update('Step 1: Cleaning project');
await runProcess('flutter clean', silent: true);
spinner.update('Step 2: Fetching dependencies');
await runProcess('flutter pub get', silent: true);
spinner.update('Step 3: Building release');
await runProcess('flutter build web --release', silent: true);
// Complete the process
spinner.stop(completionMessage: 'Deployment completed successfully', success: true);
} catch (e) {
spinner.stop(completionMessage: 'Deployment failed: $e', success: false);
}
}
L'utilisation de spinners dans vos commandes personnalisees fournit un retour visuel clair aux utilisateurs pendant les operations longues, creant une experience en ligne de commande plus polie et professionnelle.
Obtenir une valeur string des options
String getString(String name, {String defaultValue = ''})
Parametres :
name: Le nom de l'option a recupererdefaultValue: Valeur par defaut optionnelle si l'option n'est pas fournie
Retourne : La valeur de l'option sous forme de string
Exemple :
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption("name", defaultValue: "Anthony");
return command;
}
Future<void> handle(CommandResult result) async {
final name = result.getString('name');
info('Hello, $name!');
}
Obtenir une valeur booleenne des options
bool getBool(String name, {bool defaultValue = false})
Parametres :
name: Le nom de l'option a recupererdefaultValue: Valeur par defaut optionnelle si l'option n'est pas fournie
Retourne : La valeur de l'option sous forme de booleen
Exemple :
@override
CommandBuilder builder(CommandBuilder command) {
command.addFlag("verbose", defaultValue: false);
return command;
}
Future<void> handle(CommandResult result) async {
final verbose = result.getBool('verbose');
if (verbose) {
info('Verbose mode enabled');
} else {
info('Verbose mode disabled');
}
}
Obtenir une valeur entiere des options
int getInt(String name, {int defaultValue = 0})
Parametres :
name: Le nom de l'option a recupererdefaultValue: Valeur par defaut optionnelle si l'option n'est pas fournie
Retourne : La valeur de l'option sous forme d'entier
Exemple :
@override
CommandBuilder builder(CommandBuilder command) {
command.addOption("count", defaultValue: 5);
return command;
}
Future<void> handle(CommandResult result) async {
final count = result.getInt('count');
info('Count is set to $count');
}
Mettre en pause pour une duree specifiee
void sleep(int seconds)
Parametres :
seconds: Le nombre de secondes de pause
Retourne : Rien
Exemple :
@override
Future<void> handle(CommandResult result) async {
info('Sleeping for 5 seconds...');
await sleep(5);
info('Awake now!');
}
Formatage de sortie
Au-dela des methodes basiques info, error, success et warning, NyCustomCommand fournit des helpers de sortie supplementaires :
@override
Future<void> handle(CommandResult result) async {
// Print plain text (no color)
line('Processing your request...');
// Print blank lines
newLine(); // one blank line
newLine(3); // three blank lines
// Print a muted comment (gray text)
comment('This is a background note');
// Print a prominent alert box
alert('Important: Please read carefully');
// Ask is an alias for prompt
final name = ask('What is your name?');
// Hidden input for sensitive data (e.g., passwords, API keys)
final apiKey = promptSecret('Enter your API key:');
// Abort the command with an error message and exit code
if (name.isEmpty) {
abort('Name is required'); // exits with code 1
}
}
| Methode | Description |
|---|---|
line(String message) |
Afficher du texte brut sans couleur |
newLine([int count = 1]) |
Afficher des lignes vides |
comment(String message) |
Afficher du texte attenue/gris |
alert(String message) |
Afficher une boite d'alerte proeminente |
ask(String question, {String defaultValue}) |
Alias pour prompt |
promptSecret(String question) |
Saisie masquee pour les donnees sensibles |
abort([String? message, int exitCode = 1]) |
Quitter la commande avec une erreur |
Helpers du systeme de fichiers
NyCustomCommand inclut des helpers de systeme de fichiers integres pour que vous n'ayez pas a importer manuellement dart:io pour les operations courantes.
Lecture et ecriture de fichiers
@override
Future<void> handle(CommandResult result) async {
// Check if a file exists
if (fileExists('lib/config/app.dart')) {
info('Config file found');
}
// Check if a directory exists
if (directoryExists('lib/app/models')) {
info('Models directory found');
}
// Read a file (async)
String content = await readFile('pubspec.yaml');
// Read a file (sync)
String contentSync = readFileSync('pubspec.yaml');
// Write to a file (async)
await writeFile('lib/generated/output.dart', 'class Output {}');
// Write to a file (sync)
writeFileSync('lib/generated/output.dart', 'class Output {}');
// Append content to a file
await appendFile('log.txt', 'New log entry\n');
// Ensure a directory exists (creates it if missing)
await ensureDirectory('lib/generated');
// Delete a file
await deleteFile('lib/generated/output.dart');
// Copy a file
await copyFile('lib/config/app.dart', 'lib/config/app.bak.dart');
}
| Methode | Description |
|---|---|
fileExists(String path) |
Retourne true si le fichier existe |
directoryExists(String path) |
Retourne true si le repertoire existe |
readFile(String path) |
Lire un fichier comme string (async) |
readFileSync(String path) |
Lire un fichier comme string (sync) |
writeFile(String path, String content) |
Ecrire du contenu dans un fichier (async) |
writeFileSync(String path, String content) |
Ecrire du contenu dans un fichier (sync) |
appendFile(String path, String content) |
Ajouter du contenu a un fichier |
ensureDirectory(String path) |
Creer le repertoire s'il n'existe pas |
deleteFile(String path) |
Supprimer un fichier |
copyFile(String source, String destination) |
Copier un fichier |
Helpers JSON et YAML
Lire et ecrire des fichiers JSON et YAML avec les helpers integres.
@override
Future<void> handle(CommandResult result) async {
// Read a JSON file as a Map
Map<String, dynamic> config = await readJson('config.json');
// Read a JSON file as a List
List<dynamic> items = await readJsonArray('lib/app/commands/commands.json');
// Write data to a JSON file (pretty printed by default)
await writeJson('output.json', {'name': 'MyApp', 'version': '1.0.0'});
// Write compact JSON
await writeJson('output.json', data, pretty: false);
// Append an item to a JSON array file
// If the file contains [{"name": "a"}], this adds to that array
await appendToJsonArray(
'lib/app/commands/commands.json',
{'name': 'my_command', 'category': 'app', 'script': 'my_command.dart'},
uniqueKey: 'name', // prevents duplicates by this key
);
// Read a YAML file as a Map
Map<String, dynamic> pubspec = await readYaml('pubspec.yaml');
info('Project: ${pubspec['name']}');
}
| Methode | Description |
|---|---|
readJson(String path) |
Lire un fichier JSON comme Map<String, dynamic> |
readJsonArray(String path) |
Lire un fichier JSON comme List<dynamic> |
writeJson(String path, dynamic data, {bool pretty = true}) |
Ecrire des donnees en JSON |
appendToJsonArray(String path, Map item, {String? uniqueKey}) |
Ajouter a un fichier tableau JSON |
readYaml(String path) |
Lire un fichier YAML comme Map<String, dynamic> |
Helpers de conversion de casse
Convertir des strings entre les conventions de nommage sans importer le package recase.
@override
Future<void> handle(CommandResult result) async {
String input = 'user profile page';
info(snakeCase(input)); // user_profile_page
info(camelCase(input)); // userProfilePage
info(pascalCase(input)); // UserProfilePage
info(titleCase(input)); // User Profile Page
info(kebabCase(input)); // user-profile-page
info(constantCase(input)); // USER_PROFILE_PAGE
}
| Methode | Format de sortie | Exemple |
|---|---|---|
snakeCase(String input) |
snake_case |
user_profile |
camelCase(String input) |
camelCase |
userProfile |
pascalCase(String input) |
PascalCase |
UserProfile |
titleCase(String input) |
Title Case |
User Profile |
kebabCase(String input) |
kebab-case |
user-profile |
constantCase(String input) |
CONSTANT_CASE |
USER_PROFILE |
Helpers de chemins projet
Getters pour les repertoires de projet Nylo Website standard. Ceux-ci retournent des chemins relatifs a la racine du projet.
@override
Future<void> handle(CommandResult result) async {
info(modelsPath); // lib/app/models
info(controllersPath); // lib/app/controllers
info(widgetsPath); // lib/resources/widgets
info(pagesPath); // lib/resources/pages
info(commandsPath); // lib/app/commands
info(configPath); // lib/config
info(providersPath); // lib/app/providers
info(eventsPath); // lib/app/events
info(networkingPath); // lib/app/networking
info(themesPath); // lib/resources/themes
// Build a custom path relative to the project root
String customPath = projectPath('lib/app/services/auth_service.dart');
}
| Propriete | Chemin |
|---|---|
modelsPath |
lib/app/models |
controllersPath |
lib/app/controllers |
widgetsPath |
lib/resources/widgets |
pagesPath |
lib/resources/pages |
commandsPath |
lib/app/commands |
configPath |
lib/config |
providersPath |
lib/app/providers |
eventsPath |
lib/app/events |
networkingPath |
lib/app/networking |
themesPath |
lib/resources/themes |
projectPath(String relativePath) |
Resoudre un chemin relatif dans le projet |
Helpers de plateforme
Verifier la plateforme et acceder aux variables d'environnement.
@override
Future<void> handle(CommandResult result) async {
// Platform checks
if (isWindows) {
info('Running on Windows');
} else if (isMacOS) {
info('Running on macOS');
} else if (isLinux) {
info('Running on Linux');
}
// Current working directory
info('Working in: $workingDirectory');
// Read system environment variables
String home = env('HOME', '/default/path');
}
| Propriete / Methode | Description |
|---|---|
isWindows |
true si execute sur Windows |
isMacOS |
true si execute sur macOS |
isLinux |
true si execute sur Linux |
workingDirectory |
Chemin du repertoire de travail actuel |
env(String key, [String defaultValue = '']) |
Lire une variable d'environnement systeme |
Commandes Dart et Flutter
Executer les commandes CLI courantes Dart et Flutter comme methodes d'aide. Chacune retourne le code de sortie du processus.
@override
Future<void> handle(CommandResult result) async {
// Format a Dart file or directory
await dartFormat('lib/app/models/user.dart');
// Run dart analyze
int analyzeResult = await dartAnalyze('lib/');
// Run flutter pub get
await flutterPubGet();
// Run flutter clean
await flutterClean();
// Build for a target with additional args
await flutterBuild('apk', args: ['--release', '--split-per-abi']);
await flutterBuild('web', args: ['--release']);
// Run flutter test
await flutterTest();
await flutterTest('test/unit/'); // specific directory
}
| Methode | Description |
|---|---|
dartFormat(String path) |
Executer dart format sur un fichier ou repertoire |
dartAnalyze([String? path]) |
Executer dart analyze |
flutterPubGet() |
Executer flutter pub get |
flutterClean() |
Executer flutter clean |
flutterBuild(String target, {List<String> args}) |
Executer flutter build <target> |
flutterTest([String? path]) |
Executer flutter test |
Manipulation de fichiers Dart
Helpers pour editer programmatiquement des fichiers Dart, utiles lors de la creation d'outils de scaffolding.
@override
Future<void> handle(CommandResult result) async {
// Add an import statement to a Dart file (avoids duplicates)
await addImport(
'lib/bootstrap/providers.dart',
"import '/app/providers/firebase_provider.dart';",
);
// Insert code before the last closing brace in a file
// Useful for adding entries to registration maps
await insertBeforeClosingBrace(
'lib/bootstrap/providers.dart',
' FirebaseProvider(),',
);
// Check if a file contains a specific string
bool hasImport = await fileContains(
'lib/bootstrap/providers.dart',
'firebase_provider',
);
// Check if a file matches a regex pattern
bool hasClass = await fileContainsPattern(
'lib/app/models/user.dart',
RegExp(r'class User'),
);
}
| Methode | Description |
|---|---|
addImport(String filePath, String importStatement) |
Ajouter un import a un fichier Dart (ignore si deja present) |
insertBeforeClosingBrace(String filePath, String code) |
Inserer du code avant la derniere } dans un fichier |
fileContains(String filePath, String identifier) |
Verifier si un fichier contient une string |
fileContainsPattern(String filePath, Pattern pattern) |
Verifier si un fichier correspond a un pattern |
Helpers de repertoires
Helpers pour travailler avec des repertoires et trouver des fichiers.
@override
Future<void> handle(CommandResult result) async {
// List directory contents
var entities = listDirectory('lib/app/models');
for (var entity in entities) {
info(entity.path);
}
// List recursively
var allEntities = listDirectory('lib/', recursive: true);
// Find files matching criteria
List<File> dartFiles = findFiles(
'lib/app/models',
extension: '.dart',
recursive: true,
);
// Find files by name pattern
List<File> testFiles = findFiles(
'test/',
namePattern: RegExp(r'_test\.dart$'),
);
// Delete a directory recursively
await deleteDirectory('build/');
// Copy a directory (recursive)
await copyDirectory('lib/templates', 'lib/generated');
}
| Methode | Description |
|---|---|
listDirectory(String path, {bool recursive = false}) |
Lister le contenu d'un repertoire |
findFiles(String directory, {String? extension, Pattern? namePattern, bool recursive = true}) |
Trouver des fichiers correspondant aux criteres |
deleteDirectory(String path) |
Supprimer un repertoire recursivement |
copyDirectory(String source, String destination) |
Copier un repertoire recursivement |
Helpers de validation
Helpers pour valider et nettoyer les saisies utilisateur pour la generation de code.
@override
Future<void> handle(CommandResult result) async {
// Validate a Dart identifier
if (!isValidDartIdentifier('MyClass')) {
error('Invalid Dart identifier');
}
// Require a non-empty first argument
String name = requireArgument(result, message: 'Please provide a name');
// Clean a class name (PascalCase, remove suffixes)
String className = cleanClassName('user_model', removeSuffixes: ['_model']);
// Returns: 'User'
// Clean a file name (snake_case with extension)
String fileName = cleanFileName('UserModel', extension: '.dart');
// Returns: 'user_model.dart'
}
| Methode | Description |
|---|---|
isValidDartIdentifier(String name) |
Valider un nom d'identifiant Dart |
requireArgument(CommandResult result, {String? message}) |
Exiger un premier argument non vide ou abandonner |
cleanClassName(String name, {List<String> removeSuffixes}) |
Nettoyer et mettre en PascalCase un nom de classe |
cleanFileName(String name, {String extension = '.dart'}) |
Nettoyer et mettre en snake_case un nom de fichier |
Scaffolding de fichiers
Creer un ou plusieurs fichiers avec du contenu en utilisant le systeme de scaffolding.
Fichier unique
@override
Future<void> handle(CommandResult result) async {
await scaffold(
path: 'lib/app/services/auth_service.dart',
content: '''
class AuthService {
Future<bool> login(String email, String password) async {
// TODO: implement login
return false;
}
}
''',
force: false, // don't overwrite if exists
successMessage: 'AuthService created',
);
}
Fichiers multiples
@override
Future<void> handle(CommandResult result) async {
await scaffoldMany([
ScaffoldFile(
path: 'lib/app/models/product.dart',
content: 'class Product {}',
successMessage: 'Product model created',
),
ScaffoldFile(
path: 'lib/app/networking/product_api_service.dart',
content: 'class ProductApiService {}',
successMessage: 'Product API service created',
),
], force: false);
}
La classe ScaffoldFile accepte :
| Propriete | Type | Description |
|---|---|---|
path |
String |
Chemin du fichier a creer |
content |
String |
Contenu du fichier |
successMessage |
String? |
Message affiche en cas de succes |
Executeur de taches
Executer une serie de taches nommees avec une sortie de statut automatique.
Executeur de taches basique
@override
Future<void> handle(CommandResult result) async {
await runTasks([
CommandTask(
'Clean project',
() => runProcess('flutter clean', silent: true),
),
CommandTask(
'Fetch dependencies',
() => runProcess('flutter pub get', silent: true),
),
CommandTask(
'Run tests',
() => runProcess('flutter test', silent: true),
stopOnError: true, // stop pipeline if this fails (default)
),
]);
}
Executeur de taches avec Spinner
@override
Future<void> handle(CommandResult result) async {
await runTasksWithSpinner([
CommandTask(
name: 'Preparing release',
action: () async {
await flutterClean();
await flutterPubGet();
},
),
CommandTask(
name: 'Building APK',
action: () => flutterBuild('apk', args: ['--release']),
),
]);
}
La classe CommandTask accepte :
| Propriete | Type | Defaut | Description |
|---|---|---|---|
name |
String |
requis | Nom de la tache affiche dans la sortie |
action |
Future<void> Function() |
requis | Fonction asynchrone a executer |
stopOnError |
bool |
true |
Arreter les taches restantes en cas d'echec |
Sortie en tableau
Afficher des tableaux ASCII formates dans la console.
@override
Future<void> handle(CommandResult result) async {
table(
['Name', 'Version', 'Status'],
[
['nylo_framework', '7.0.0', 'installed'],
['nylo_support', '7.0.0', 'installed'],
['dio', '5.4.0', 'installed'],
],
);
}
Sortie :
┌─────────────────┬─────────┬───────────┐
│ Name │ Version │ Status │
├─────────────────┼─────────┼───────────┤
│ nylo_framework │ 7.0.0 │ installed │
│ nylo_support │ 7.0.0 │ installed │
│ dio │ 5.4.0 │ installed │
└─────────────────┴─────────┴───────────┘
Barre de progression
Afficher une barre de progression pour les operations avec un nombre d'elements connu.
Barre de progression manuelle
@override
Future<void> handle(CommandResult result) async {
// Create a progress bar for 100 items
final progress = progressBar(100, message: 'Processing files');
progress.start();
for (int i = 0; i < 100; i++) {
await Future.delayed(Duration(milliseconds: 50));
progress.tick(); // increment by 1
}
progress.complete('All files processed');
}
Traiter des elements avec progression
@override
Future<void> handle(CommandResult result) async {
final files = findFiles('lib/', extension: '.dart');
// Process items with automatic progress tracking
final results = await withProgress<File, String>(
items: files,
process: (file, index) async {
// process each file
return file.path;
},
message: 'Analyzing Dart files',
completionMessage: 'Analysis complete',
);
info('Processed ${results.length} files');
}
Progression synchrone
@override
Future<void> handle(CommandResult result) async {
final items = ['a', 'b', 'c', 'd', 'e'];
final results = withProgressSync<String, String>(
items: items,
process: (item, index) {
// synchronous processing
return item.toUpperCase();
},
message: 'Converting items',
);
info('Results: $results');
}
La classe ConsoleProgressBar fournit :
| Methode | Description |
|---|---|
start() |
Demarrer la barre de progression |
tick([int amount = 1]) |
Incrementer la progression |
update(int value) |
Definir la progression a une valeur specifique |
updateMessage(String newMessage) |
Changer le message affiche |
complete([String? completionMessage]) |
Terminer avec un message optionnel |
stop() |
Arreter sans terminer |
current |
Valeur de progression actuelle (getter) |
percentage |
Progression en pourcentage (getter) |