Saltar al contenido principal

Agrega interactividad a tu aplicación

Agrega comandos simples a tu aplicación cli. Aprende los fundamentos de la sintaxis de Dart incluyendo flujo de control, colecciones, variables, funciones y más.

En este capítulo, obtendrás práctica directa con la sintaxis de Dart. Aprenderás a leer la entrada del usuario, imprimir información de uso y crear una interacción básica de línea de comandos.

Qué lograrás

Construir manejadores de comandos con sentencias if
Trabajar con listas y variables
Tener en cuenta la nulabilidad en tu código
Definir y llamar funciones
Leer y manejar la entrada del usuario

Prerrequisitos

#

Antes de comenzar este capítulo, asegúrate de tener:

  • Completado el Capítulo 1 y tener un entorno de desarrollo de Dart en funcionamiento.
  • Familiaridad con conceptos básicos de programación (variables, tipos de datos, flujo de control).

Tareas

#

Agrega alguna funcionalidad básica a tu aplicación de línea de comandos Dartpedia y luego explora la sintaxis de Dart para ella.

Tarea 1: Implementar los comandos version y help

#
  1. Implementa el comando version en cli/bin/cli.dart: Agrega lógica para manejar un comando version, el cual imprime la versión actual de la CLI. Usa una sentencia if para verificar si el primer argumento proporcionado es version. También necesitarás una constante version.

    Primero, por encima de tu función main, declara una variable const para la versión. El valor de una variable const nunca se puede cambiar después de haber sido establecido:

    dart
    const version = '0.0.1'; // Add this line
    

    A continuación, modifica tu función main para verificar el argumento version:

    dart
    void main(List<String> arguments) {
      if (arguments.isEmpty) {
        print('Hello, Dart!');
      } else if (arguments.first == 'version') {
        print('Dartpedia CLI version $version');
      }
    }
    

    La sintaxis $version se llama interpolación de cadenas. Te permite incrustar el valor de la variable directamente en una cadena prefijando el nombre de la variable con un signo $.

  2. Prueba el comando version: Ejecuta tu aplicación con el argumento version:

    bash
    dart bin/cli.dart version
    

    Ahora deberías ver:

    bash
    Dartpedia CLI version 0.0.1
    

    Si ejecutas tu aplicación sin argumentos, aún verás "Hello, Dart!".

  3. Agrega una función printUsage: Para hacer la salida más amigable para el usuario, crea una función separada para mostrar la información de uso. Coloca esta función fuera y debajo de tu función main.

    dart
    void printUsage() { // Add this new function
      print(
        "The following commands are valid: 'help', 'version', 'search <ARTICLE-TITLE>'"
      );
    }
    

    search es el comando que eventualmente buscará en Wikipedia.

  4. Implementa el comando help y refina main: Ahora, integra el comando help usando una sentencia else if, y limpia el comportamiento predeterminado para llamar a la función printUsage.

    Modifica tu función main para que se vea así:

    dart
    void main(List<String> arguments) {
      if (arguments.isEmpty || arguments.first == 'help') {
        printUsage(); // Change this from 'Hello, Dart!'
      } else if (arguments.first == 'version') {
        print('Dartpedia CLI version $version');
      } else {
        printUsage(); // Catch-all for any unrecognized command.
      }
    }
    
  5. Comprende la estructura if/else y las variables: Ahora que has implementado el flujo de control en la función main, revisa el código que se agregó para ello.

    • arguments.isEmpty comprueba si no se proporcionaron argumentos de línea de comandos.
    • arguments.first accede al primerísimo argumento, que estás usando como nuestro comando.
    • version se declara como una const. Esto significa que su valor se conoce en tiempo de compilación y no puedes cambiarlo durante el tiempo de ejecución.
    • arguments es una variable normal (no constante) porque su contenido puede cambiar durante el tiempo de ejecución en función de la entrada del usuario.

    Ejecuta tu aplicación con el argumento help. Deberías ver impresa la información de uso:

    bash
    dart bin/cli.dart help
    

    Además, intenta ejecutarlo sin ningún argumento:

    bash
    dart bin/cli.dart
    

    Ten en cuenta que continúa mostrando la información de uso. En este punto, cualquier comando que no hayas definido también imprimirá la información de uso. Este es el comportamiento esperado por ahora.

Tarea 2: Implementar el comando search

#

Next, implementa un comando search básico que tome como entrada el título de un artículo. A medida que construyes esta funcionalidad, trabajarás con manipulación de List, comprobaciones de nulos e interpolación de cadenas.

  1. Integra el comando search en main: Primero, modifica la función main en cli/bin/cli.dart para incluir una rama else if que maneje el comando search. Por ahora, solo imprime un mensaje de marcador de posición.

    dart
    void main(List<String> arguments) {
      if (arguments.isEmpty || arguments.first == 'help') {
        printUsage();
      } else if (arguments.first == 'version') {
        print('Dartpedia CLI version $version');
      } else if (arguments.first == 'search') {
        // Add this new block:
        print('Search command recognized!');
      } else {
        printUsage();
      }
    }
    
  2. Prueba el nuevo comando: Ejecuta tu aplicación con el comando search:

    bash
    dart bin/cli.dart search
    

    Deberías ver:

    bash
    Search command recognized!
    
  3. Define la función searchWikipedia: El comando search eventualmente ejecutará la lógica central de tu aplicación llamando a una función llamada searchWikipedia. Por ahora, haz que searchWikipedia imprima los argumentos que se le pasan con el comando search. Coloca esta nueva función debajo de main.

    dart
    // ... (your existing main function)
    
    void searchWikipedia(List<String>? arguments) { // Add this new function and add ? to arguments type
      print('searchWikipedia received arguments: $arguments');
    }
    
    // ... (your existing printUsage() function)
    

    Aspectos destacados del código anterior:

    • List<String>? arguments significa que la propia lista arguments puede ser null.

  4. Llama a la función searchWikipedia desde la función main: Ahora, modifica el bloque del comando search en main para llamar a searchWikipedia y pasarle cualquier argumento que venga después del propio comando search. Usa arguments.sublist(1) para obtener todos los argumentos a partir del segundo. Si no se proporcionan argumentos después de search, pasa null a searchWikipedia.

    dart
    void main(List<String> arguments) {
      if (arguments.isEmpty || arguments.first == 'help') {
        printUsage();
      } else if (arguments.first == 'version') {
        print('Dartpedia CLI version $version');
      } else if (arguments.first == 'search') {
        // Add this new block:
        final inputArgs = arguments.length > 1 ? arguments.sublist(1) : null;
        searchWikipedia(inputArgs);
      } else {
        printUsage();
      }
    }
    

    Aspectos destacados del código anterior:

    • Las variables final solo se pueden establecer una vez y se usan cuando nunca se tiene la intención de volver a cambiar la variable en el código.
    • arguments.sublist(1) crea una nueva lista que contiene todos los elementos de la lista arguments después del primer elemento (que era search).
    • arguments.length > 1 ? ... : null; es un operador condicional (ternario). Asegura que si no se proporcionan argumentos después del comando search, inputArgs se convierte en null, coincidiendo con el comportamiento del código de ejemplo para el parámetro arguments de searchWikipedia que es de tipo List<String>?.
  5. Prueba searchWikipedia con argumentos: Usando la línea de comandos, ejecuta la aplicación con el título de un artículo de prueba:

    bash
    dart bin/cli.dart search Dart Programming
    

    Deberías ver:

    bash
    searchWikipedia received arguments: [Dart, Programming]
    

    A continuación, ejecuta el mismo comando sin los argumentos adicionales:

    bash
    dart bin/cli.dart search
    

    Deberías ver:

    bash
    searchWikipedia received arguments: null
    
  6. Maneja el título del artículo faltante y la entrada del usuario con el comando stdin: Es más amigable para el usuario solicitarle información si no proporciona el título de un artículo en la línea de comandos. Usa stdin.readLineSync() para esto.

    Primero, añade la importación necesaria en la parte superior de tu archivo cli/bin/cli.dart:

    dart
    import 'dart:io'; // Add this line at the top
    

    dart:io es una librería principal del SDK de Dart y proporciona APIs para trabajar con archivos, directorios, sockets, clientes y servidores HTTP, y más.

    Ahora, actualiza tu función searchWikipedia.

    dart
    void searchWikipedia(List<String>? arguments) {
      final String articleTitle;
    
      // If the user didn't pass in arguments, request an article title.
      if (arguments == null || arguments.isEmpty) {
        print('Please provide an article title.');
        // Await input and provide a default empty string if the input is null.
        articleTitle = stdin.readLineSync() ?? '';
      } else {
        // Otherwise, join the arguments into a single string.
        articleTitle = arguments.join(' ');
      }
    
      print('Current article title: $articleTitle');
    }
    

    Este bloque de código anterior introduce algunos conceptos clave:

    • Declara una variable final String articleTitle. Esto permite que el análisis estático detecte que articleTitle será un String y no será nulo.
    • Luego, una sentencia if/else comprueba si se proporcionaron argumentos de línea de comandos para la búsqueda.
    • Si faltan argumentos, solicita información al usuario, lee la entrada usando stdin.readLineSync() y maneja de forma segura los casos en los que no se proporciona ninguna entrada.
    • Si los argumentos están presentes, usa arguments.join(' ') para combinarlos en una sola cadena de búsqueda.

    Aspectos destacados del código anterior:

    • stdin.readLineSync() ?? '' lee la entrada del usuario. Aunque stdin.readLineSync() puede devolver nulo, el operador de coalescencia nula (??) se usa para proporcionar una cadena vacía ('') como alternativa si la entrada es nula. Esta es una forma concisa de asegurar que la variable sea una cadena no nula.
    • arguments.join(' ') concatena todos los elementos de la lista arguments en una sola cadena, usando un espacio como separador. Por ejemplo, ['Dart', 'Programming'] se convierte en "Dart Programming". Esto es crucial para tratar las entradas de línea de comandos de varias palabras como una única frase de búsqueda.
    • El análisis estático de Dart puede detectar que articleTitle se garantiza que se inicializará cuando se ejecute la sentencia print. Sin importar qué camino se tome a través del cuerpo de esta función, la variable no acepta nulos (non-nullable).
  7. Termina searchWikipedia para imprimir resultados de búsqueda simulados: Actualiza searchWikipedia para mostrar mensajes que parezcan que nuestro programa encontró algo. Esto nos ayuda a ver lo que hará nuestro programa terminado sin construir todo realmente en este momento. Solo verás estos mensajes si incluyes una consulta de búsqueda al ejecutar el programa.

    Por ejemplo: dart bin/cli.dart search Dart Programming.

    dart
    void searchWikipedia(List<String>? arguments) {
      final String articleTitle;
    
      // If the user didn't pass in arguments, request an article title.
      if (arguments == null || arguments.isEmpty) {
        print('Please provide an article title.');
        // Await input and provide a default empty string if the input is null.
        articleTitle = stdin.readLineSync() ?? '';
      } else {
        // Otherwise, join the arguments into a single string.
        articleTitle = arguments.join(' ');
      }
    
      print('Looking up articles about "$articleTitle". Please wait.');
      print('Here ya go!');
      print('(Pretend this is an article about "$articleTitle")');
    }
    
  8. Final Test Run with both scenarios:

    Ahora que la simulación del artículo está configurada, prueba la función searchWikipedia de diferentes maneras:

    bash
    dart bin/cli.dart search Dart Programming
    

    Deberías ver:

    bash
    Looking up articles about "Dart Programming". Please wait.
    Here ya go!
    (Pretend this is an article about "Dart Programming")
    

    Ejecutar sin argumentos (escribe "Flutter Framework" cuando se te solicite):

    bash
    dart bin/cli.dart search
    
    bash
    Please provide an article title.
    Flutter Framework
    

    Ahora has construido con éxito el comando search básico con el manejo de la entrada del usuario, tratando correctamente las entradas de la línea de comandos de varias palabras como una sola frase de búsqueda en la salida.

Revisión

#

Qué lograste

Aquí tienes un resumen de lo que construiste y aprendiste en esta lección.
Construiste manejadores de comandos con sentencias if y else

Usaste sentencias if y else para enrutar diferentes comandos como help, version y search. Este patrón de flujo de control es fundamental para construir aplicaciones de CLI interactivas que respondan a la entrada del usuario.

Trabajaste con listas y variables

Aprendiste a declarar variables con var, final y const, y trabajaste con listas usando .isEmpty, .first, .sublist() y .join(). También usaste la interpolación de cadenas ($variable) para incrustar valores en literales de cadena.

Tuviste en cuenta la nulabilidad en tu código

Adoptaste la seguridad contra nulos (sound null safety) de Dart mediante el uso de tipos que aceptan nulos (?), comprobaciones de nulos y el operador de coalescencia nula (??) para proporcionar valores predeterminados cuando las expresiones puedan ser null.

Definiste y llamaste funciones

Creaste las funciones printUsage() y searchWikipedia() para organizar tu código en unidades reutilizables. Separar la lógica reutilizable en funciones ayuda a mantener limpia la función main y hace que tu código sea más fácil de leer y mantener.

Leíste y manejaste la entrada del usuario

Usaste stdin.readLineSync() de dart:io para leer la entrada desde la terminal. Esto permite solicitudes interactivas como una alternativa o además de los argumentos de la línea de comandos.

Cuestionario

#

Pon a prueba tu comprensión

1 / 3
Which keyword is used to declare a constant variable in Dart whose value is known at compile time?
  1. static

    No exactamente.

    static se usa para miembros a nivel de clase, no para declarar constantes. Sin embargo, las constantes a nivel de clase también deben ser static.

  2. const

    ¡Correcto!

    const declara una constante en tiempo de compilación. El valor debe ser conocido en tiempo de compilación y nunca puede cambiar.

  3. final

    No exactamente.

    final crea una variable que solo se puede inicializar una vez, pero su valor se determina en tiempo de ejecución, no en tiempo de compilación.

  4. var

    No exactamente.

    Las variables declaradas con var se pueden reasignar y no es necesario conocerlas en tiempo de compilación.

What is the primary purpose of stdin.readLineSync() in a CLI application?
  1. Para leer una sola línea de entrada de texto del usuario.

    ¡Correcto!

    stdin.readLineSync() lee una sola línea de texto de la entrada estándar, permitiendo que tu programa reciba la entrada del usuario.

  2. Para imprimir la salida en la consola.

    No exactamente.

    La impresión de la salida se realiza con print(), stdout.write() y otros métodos similares.

  3. Para verificar si un archivo existe.

    No exactamente.

    La existencia de archivos se comprueba usando File.existsSync() o métodos similares.

  4. Para ejecutar un comando.

    No exactamente.

    La ejecución de comandos se realiza mediante Process.run() o métodos similares.

What does arguments.sublist(1) return when arguments is ['search', 'Dart', 'Programming']?
  1. ['Programming']

    No exactamente.

    Este es solo el último elemento. El argumento 1 especifica el índice inicial, no cuántos elementos omitir del final.

  2. ['search']

    No exactamente.

    sublist(1) devuelve elementos a partir del índice 1 (el segundo elemento), no solo el primer elemento.

  3. ['search', 'Dart']

    No exactamente.

    Esta respuesta incluye 'search', pero sublist(1) comienza desde el índice 1, lo que excluye el elemento en el índice 0.

  4. ['Dart', 'Programming']

    ¡Correcto!

    sublist(1) crea una nueva lista que contiene todos los elementos desde el índice 1 en adelante, omitiendo el primer elemento.

Siguiente lección

#

En el próximo capítulo, te sumergirás en la programación asíncrona y aprenderás cómo obtener datos de la API de Wikipedia usando el paquete http. Esto permitirá que tu aplicación recupere datos reales y se los muestre al usuario.