Escribe tu primera app de Flutter (parte 1)

Flutter es el kit de herramientas de IU de Google diseñado para crear aplicaciones atractivas compiladas de forma nativa que funcionen en dispositivos móviles, la Web y computadoras de escritorio a partir de una base de código única. Flutter es gratuito y de código abierto; además, funciona con el código existente. Lo utilizan desarrolladores y organizaciones de todo el mundo.

En este codelab, crearás una app de Flutter simple para dispositivos móviles. Si ya trabajaste con código orientado a objetos y conoces los conceptos básicos de programación (como variables, bucles y condicionales), podrás completarlo. No es necesario que tengas experiencia con programación web, móvil o Dart.

Qué aprenderás en la parte 1

  • Cómo escribir una app de Flutter que se vea natural tanto en iOS como en Android y la Web
  • La estructura básica de una app de Flutter
  • Cómo buscar y usar paquetes para extender la funcionalidad
  • Cómo usar la recarga en caliente (hot reload) y agilizar el ciclo de desarrollo
  • Cómo implementar un widget con estado
  • Cómo crear una lista infinita de carga diferida

En la parte 2 de este codelab, agregarás interactividad, modificarás el tema de la app y añadirás la capacidad de navegar a una página nueva (denominada ruta en Flutter).

Qué compilarás en la parte 1

Implementarás una app simple que genere nombres propuestos para una empresa startup. El usuario podrá seleccionar nombres y anular selecciones para guardar los mejores. El código genera 10 nombres a la vez de forma diferida. A medida que el usuario se desplaza, la app genera más nombres. No habrá un límite para el desplazamiento del usuario.

En el siguiente GIF animado, se muestra cómo funciona la app al completar esta parte.

6556f8b61acd6a89.gif

¿Qué te gustaría aprender en este codelab?

Desconozco el tema y me gustaría obtener una buena descripción general. Tengo algunos conocimientos sobre este tema, pero me gustaría repasarlos. Estoy buscando código de ejemplo para usar en mi proyecto. Estoy buscando una explicación sobre un tema específico.

Para completar este codelab, necesitas dos tipos de software: el SDK de Flutter y un editor. (En el codelab, se supone que utilizas Android Studio, pero puedes usar tu editor preferido).

Puedes ejecutar el codelab en cualquiera de los siguientes dispositivos:

  • Un dispositivo físico Android o iOS conectado a tu computadora y configurado en el modo de desarrollador
  • El simulador de iOS (requiere la instalación de herramientas de Xcode)
  • Android Emulator (requiere configuración en Android Studio)
  • Un navegador (para la depuración, se requiere Chrome)

b2f84ff91b0e1396.png Crea una app de Flutter simple a partir de una plantilla y un proyecto de Flutter con el nombre startup_namer, y migra a seguridad nula como se muestra a continuación.

$ flutter create startup_namer
$ cd startup_namer
$ dart migrate --apply-changes

En la mayor parte, editarás lib/main.dart, donde se encuentra el código Dart.

b2f84ff91b0e1396.png Reemplaza el contenido de lib/main.dart. Borra todo el código de lib/main.dart y reemplázalo con el que se muestra a continuación, que muestra el mensaje "Hello World" en el centro de la pantalla.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Welcome to Flutter'),
        ),
        body: const Center(
          child: const Text('Hello World'),
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Ejecuta la app. Deberías ver la salida web, de Android o de iOS, según el dispositivo que uses.

Android

iOS

cf1e10b838bf60ee.png Observaciones

  • Este ejemplo crea una app de Material. Material es un lenguaje de diseño visual estándar en dispositivos móviles y la Web. Flutter ofrece un amplio conjunto de widgets de Material.
  • El método principal utiliza la notación de flechas (=>). Úsala para funciones o métodos de una línea.
  • La app extiende StatelessWidget, que hace que la app sea un widget. En Flutter, casi todos los elementos son widgets, incluidos la alineación, el padding y el diseño.
  • El widget de Scaffold de la biblioteca de Material proporciona una barra de la app predeterminada, un título y una propiedad del cuerpo que conserva el árbol de widgets para la pantalla principal. El subárbol de widgets puede ser bastante complejo.
  • El trabajo principal de un widget es proporcionar un método build que describe cómo mostrar el widget en función de otros widgets de menor nivel.
  • El cuerpo de este ejemplo consiste en un widget de Center que contiene un widget secundario de Text. El widget de Center alinea su subárbol de widgets en el centro de la pantalla.

En este paso, comenzarás a usar un paquete configurado con código abierto llamado english_words, que contiene miles de las palabras más usadas en inglés y algunas funciones de utilidad.

Puedes encontrar el paquete english_words y muchos otros paquetes configurados con código abierto en pub.dev.

b2f84ff91b0e1396.png El archivo pubspec administra los elementos para una app de Flutter. En pubspec.yaml, agrega english_words: ^4.0.0 (english_words 4.0.0 o versiones posteriores) a la lista de dependencias:

dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^1.0.2
  english_words: ^4.0.0   # add this line

b2f84ff91b0e1396.png Mientras observas el archivo pubspec en la vista de edición de Android Studio, haz clic en Packages get. Esto incorpora el paquete en tu proyecto. Deberías ver lo siguiente en la consola:

flutter packages get
Running "flutter packages get" in startup_namer...
Process finished with exit code 0

La ejecución de dart pub get también genera automáticamente el archivo pubspec.lock con una lista de todos los paquetes que se incorporaron al proyecto y sus números de versión.

b2f84ff91b0e1396.png En lib/main.dart, importa el paquete nuevo como se muestra a continuación:

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';  // Add this line.

A medida que escribes, Android Studio te brinda sugerencias sobre bibliotecas que puedes importar. Luego, renderiza la string de importación en color gris para indicar que la biblioteca importada no se usó (todavía).

A continuación, usarás el paquete english_words para generar texto en lugar de usar la frase "Hello World".

b2f84ff91b0e1396.png Realiza los siguientes cambios:

import 'package:flutter/material.dart';
import 'package:english_words/english_words.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random(); // Add this line.
    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(                       // Drop the const, and
          //child: Text('Hello World'),     // Replace this text...
          child: Text(wordPair.asPascalCase),  // With this text.
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Si se está ejecutando la app, recarga e11f6ccd1560a28b.png en caliente para actualizar la app. (Puedes escribir r en la línea de comandos para llevar a cabo la función). Cada vez que hagas clic en la recarga en caliente o guardes el proyecto, deberías ver un par de palabras diferente (que se elige de forma aleatoria) en la app en ejecución. Esto se debe a que la vinculación de palabras se genera en el método build, que se ejecuta cada vez que MaterialApp requiere renderización, o cuando se activa o desactiva la Plataforma en Flutter Inspector.

Android

iOS

¿Tienes algún problema?

Si la app no se ejecuta correctamente, comprueba que no haya errores ortográficos. De ser necesario, usa el código que aparece en los siguientes vínculos para volver a empezar.

Los widgets sin estado son inmutables; es decir que sus propiedades no pueden cambiar, ya que todos los valores son definitivos.

Los widgets con estado conservan el estado que puede cambiar durante el ciclo de vida del widget. La implementación de un widget con estado requiere por lo menos dos clases: 1) un StatefulWidget que crea una instancia de una clase State. El objeto StatefulWidget es inmutable y puede eliminar y regenerar, pero el objeto State se preserva durante el ciclo de vida del widget.

En este paso, agregarás un widget con estado (RandomWords), que crea su propia clase State (_RandomWordsState). Luego, usarás RandomWords como elemento secundario en el widget sin estado MyApp existente.

b2f84ff91b0e1396.png Crea código estándar para un widget con estado.

Se puede ubicar en cualquier parte del archivo afuera de MyApp, pero la solución lo ubica en la parte inferior del archivo. En lib/main.dart, coloca el cursor al final de todo el código e ingresa Intro un par de veces para comenzar en una línea nueva. En tu IDE, empieza a escribir stful. En el editor, se te preguntará si deseas crear un widget Stateful (con estado). Presiona Intro para aceptar. Aparecerá el código estándar de dos clases, y el cursor se posicionará para que ingreses el nombre de tu widget sin estado.

b2f84ff91b0e1396.png Ingresa RandomWords como nombre de tu widget.

Como se puede ver en el siguiente código, el widget RandomWords no hace mucho más que crear su clase State.

Cuando ingresas el nombre RandomWords para el widget con estado, el IDE actualiza automáticamente la clase State acompañante y la denomina _RandomWordsState. De forma predeterminada, el nombre de la clase State tiene un guion bajo como prefijo. Cuando agregas un guion bajo como prefijo a un identificador, se aplica privacidad en el lenguaje Dart, y esta se considera una práctica recomendada para los objetos State.

El IDE también actualiza automáticamente la clase de estado para extender State<RandomWords>, lo que indica que usas una clase State genérica para el uso específico con RandomWords. La mayor parte de la lógica de la app se encuentra aquí; conserva el estado para el widget de RandomWords. Con esta clase, se guarda la lista de pares de palabras generadas, que se alarga de manera infinita a medida que el usuario se desplaza en ella. En la segunda parte de este codelab, se marcan como favoritos los pares de palabras que el usuario agrega o se desmarcan los que este quita de la lista mediante el ícono de corazón.

Ahora, las dos clases tienen el siguiente aspecto:

class RandomWords extends StatefulWidget {
  @override
  _RandomWordsState createState() => _RandomWordsState();
}

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

b2f84ff91b0e1396.png Actualiza el método build() en _RandomWordsState.

Reemplaza return Container(); por estas dos líneas:

class _RandomWordsState extends State<RandomWords> {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();      // NEW
    return Text(wordPair.asPascalCase);      // NEW
  }
}

b2f84ff91b0e1396.png Haz los cambios que se muestran a continuación para quitar el código de generación de palabras de MyApp:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final wordPair = WordPair.random();  // DELETE

    return MaterialApp(
      title: 'Welcome to Flutter',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Welcome to Flutter'),
        ),
        body: Center(
          //child: Text(wordPair.asPascalCase), // REPLACE with...
          child: RandomWords(),                 // ...this line
        ),
      ),
    );
  }
}

b2f84ff91b0e1396.png Vuelve a cargar la app en caliente. Esta debería comportarse como antes y mostrar una vinculación de palabras cada vez que recargas la app en caliente o la guardas.

¿Tienes algún problema?

Si la app no se ejecuta de manera correcta, puedes usar el código al que dirige el siguiente vínculo para solucionar el problema.

En este paso, expandirás _RandomWordsState para generar y mostrar una lista de vinculaciones de palabras. A medida que el usuario se desplaza en la lista (que se muestra en un widget ListView), esta se alarga de manera infinita. El constructor de fábrica builder en ListView te permite compilar de manera diferida una vista de lista a pedido.

b2f84ff91b0e1396.png Agrega algunas variables de estado a la clase _RandomWordsState.

Agrega una lista de _suggestions para guardar las vinculaciones de palabras sugeridas. Además, añade una variable _biggerFont para agrandar el tamaño de fuente.

class _RandomWordsState extends State<RandomWords> {
  final _suggestions = <WordPair>[];                 // NEW
  final _biggerFont = const TextStyle(fontSize: 18); // NEW
  ...
}

A continuación, agregarás una función _buildSuggestions() a la clase _RandomWordsState. Con este método, se compila la ListView que muestra la vinculación de palabras sugerida.

La clase ListView proporciona una propiedad de compilador (itemBuilder) que es un compilador de fábrica y una función de devolución de llamada especificada como una función anónima. Se pasan dos parámetros a la función: BuildContext y la iteración de filas i. La iteración comienza en 0 y aumenta cada vez que se llama a la función; una vez por cada vinculación de palabras sugerida. Este modelo permite que la lista de sugerencias continúe creciendo a medida que el usuario se desplaza.

b2f84ff91b0e1396.png Agrega la función _buildSuggestions entera.

En la clase _RandomWordsState, agrega la siguiente función (sin los comentarios, si prefieres):

  Widget _buildSuggestions() {
    return ListView.builder(
      padding: const EdgeInsets.all(16),
      // The itemBuilder callback is called once per suggested
      // word pairing, and places each suggestion into a ListTile
      // row. For even rows, the function adds a ListTile row for
      // the word pairing. For odd rows, the function adds a
      // Divider widget to visually separate the entries. Note that
      // the divider may be difficult to see on smaller devices.
      itemBuilder: (BuildContext _context, int i) {
        // Add a one-pixel-high divider widget before each row
        // in the ListView.
        if (i.isOdd) {
          return Divider();
        }

        // The syntax "i ~/ 2" divides i by 2 and returns an
        // integer result.
        // For example: 1, 2, 3, 4, 5 becomes 0, 1, 1, 2, 2.
        // This calculates the actual number of word pairings
        // in the ListView,minus the divider widgets.
        final int index = i ~/ 2;
        // If you've reached the end of the available word
        // pairings...
        if (index >= _suggestions.length) {
          // ...then generate 10 more and add them to the
          // suggestions list.
          _suggestions.addAll(generateWordPairs().take(10));
        }
        return _buildRow(_suggestions[index]);
      }
    );
  }

La función _buildSuggestions llama a _buildRow una vez por cada par de palabras. Esa función muestra cada par nuevo en un ListTile, lo que te permite crear filas más atractivas en la parte 2.

b2f84ff91b0e1396.png Agrega una función _buildRow a _RandomWordsState.

  Widget _buildRow(WordPair pair) {
    return ListTile(
      title: Text(
        pair.asPascalCase,
        style: _biggerFont,
      ),
    );
  }

b2f84ff91b0e1396.png Actualiza el método build para _RandomWordsState.

Cámbialo a fin de usar _buildSuggestions(), en lugar de llamar directamente a la biblioteca de generación de palabras. (Scaffold implementa el diseño visual básico de Material Design).

  @override
  Widget build(BuildContext context) {
    //final wordPair = WordPair.random(); // Delete these...
    //return Text(wordPair.asPascalCase); // ... two lines.

    return Scaffold (                     // Add from here...
      appBar: AppBar(
        title: Text('Startup Name Generator'),
      ),
      body: _buildSuggestions(),
    );                                      // ... to here.
  }

b2f84ff91b0e1396.png Actualiza el método build para MyApp (modifica el título, quita la AppBar y cambia la propiedad de inicio a un widget de RandomWords).

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Startup Name Generator',
      home: RandomWords(),
    );
  }

b2f84ff91b0e1396.png Reinicia la app. Debería aparecer una lista de vinculaciones de palabras sin importar qué tan lejos te desplaces.

Android

iOS

¿Tienes algún problema?

Si la app no se ejecuta de manera correcta, puedes usar el código al que dirige el siguiente vínculo para solucionar el problema.

¡Felicitaciones!

Completaste la parte 1 de este codelab. Si te gustaría extender la app, continúa con la parte 2, donde harás las siguientes modificaciones:

  • Agregarás interactividad.
  • Agregarás la capacidad de navegar a una ruta nueva.
  • Modificarás el color del tema.

Cuando completes la parte 2, la app tendrá el siguiente aspecto:

7fcab088cd22cff7.gif

Pasos adicionales

Consulta los siguientes recursos para obtener más información sobre el SDK de Flutter:

A continuación, se detallan otros recursos:

Además, puedes conectarte con la comunidad de Flutter.