Está en la página 1de 185

Machine Translated by Google

Machine Translated by Google

Acerca del tutorial

Flutter es un marco de código abierto para crear aplicaciones móviles de alta calidad y alto rendimiento en todos
los sistemas operativos móviles: Android e iOS. Proporciona un SDK simple, potente, eficiente y fácil de entender
para escribir aplicaciones móviles en el propio lenguaje de Google, Dart.

Este tutorial recorre los conceptos básicos del marco Flutter, la instalación de Flutter SDK, la configuración de
Android Studio para desarrollar una aplicación basada en Flutter, la arquitectura de Flutter
framework y desarrollando todo tipo de aplicaciones móviles usando Flutter framework.

Audiencia

Este tutorial está preparado para profesionales que aspiran a hacer carrera en el campo de las aplicaciones
móviles. Este tutorial está destinado a que se sienta cómodo al comenzar con el marco Flutter y sus diversas
funcionalidades.

requisitos previos
Este tutorial está escrito asumiendo que los lectores ya saben qué es un marco y que tienen un conocimiento
sólido sobre la programación orientada a objetos y un conocimiento básico sobre el marco de trabajo de Android
y la programación de Dart.

Si eres un principiante en alguno de estos conceptos, te sugerimos que revises los tutoriales relacionados con
estos primero, antes de comenzar con Flutter.

Derechos de autor y descargo de responsabilidad

@Copyright 2019 por Tutorials Point (I) Pvt. Limitado.

Todo el contenido y gráficos publicados en este e-book son propiedad de Tutorials Point (I)
privado Ltd. El usuario de este libro electrónico tiene prohibido reutilizar, retener, copiar, distribuir o volver a
publicar cualquier contenido o parte del contenido de este libro electrónico de cualquier manera sin el
consentimiento por escrito del editor.

Nos esforzamos por actualizar los contenidos de nuestro sitio web y los tutoriales de la manera más oportuna y
precisa posible; sin embargo, los contenidos pueden contener inexactitudes o errores. Tutoriales Punto (I) Pvt.
Ltd. no ofrece ninguna garantía con respecto a la precisión, puntualidad o integridad de nuestro sitio
web o su contenido, incluido este tutorial. Si descubre algún error en nuestro sitio web o en este
tutorial, avísenos a contact@tutorialspoint.com

I
Machine Translated by Google

Aleteo

Tabla de contenido
Sobre el Tutorial .............................................................. .................................................... ...........................................I

Audiencia................................................. .................................................... .................................................... ......I

Requisitos previos.................................................. .................................................... .................................................... I

Derechos de autor y descargo de responsabilidad ............................................. .................................................... ....................................I

Tabla de contenido............................................... .................................................... ............................................ii

1. FLUTTER – INTRODUCCIÓN ............................................... .................................................... ... 1

Características de Flutter .................................................. .................................................... ..........................................1

Ventajas de Flutter .................................................. .................................................... .....................................2

Desventajas de Flutter ............................................... .................................................... ..................................2

2. FLUTTER – INSTALACIÓN ............................................... .................................................... ...... 3

Instalación en Windows................................................... .................................................... ....................................3

Instalación en Mac OS .................................................. .................................................... ......................................4

3. FLUTTER: CREACIÓN DE UNA APLICACIÓN SIMPLE EN ANDROID STUDIO ....................................... 5

4. FLUTTER – ARQUITECTURA DE LA APLICACIÓN DE FLUTTER ........................................... ............. 12

Widgets .................................................. .................................................... .................................................... ....12

Gestos .................................................. .................................................... .................................................... ...13

Concepto de Estado ............................................... .................................................... ..........................................13

Capas................................................. .................................................... .................................................... .......13

5. FLUTTER – INTRODUCCIÓN A LA PROGRAMACIÓN DE DART ........................................... ............. 15

Variables y tipos de datos ............................................... .................................................... ...............................15

Toma de decisiones y bucles .............................................. .................................................... ...........................dieciséis

Funciones.................................................. .................................................... .................................................... ..dieciséis

Programación orientada a objetos............................................... .................................................... ......................17

6. FLUTTER: INTRODUCCIÓN A LOS WIDGETS ........................................... .......................... 18

yl
Machine Translated by Google

Aleteo

Visualización de creación de widgets ............................................... .................................................... ............................19

7. FLUTTER – INTRODUCCIÓN A LOS DISEÑOS ........................................... ............................. 26

Tipo de widgets de diseño ............................................... .................................................... ..........................26

Widgets para un solo hijo ............................................... .................................................... .....................................26

Múltiples widgets para niños ............................................... .................................................... ..........................30

Aplicación de diseño avanzado ............................................... .................................................... .......................31

8. FLUTTER – INTRODUCCIÓN A LOS GESTOS ........................................... ............................... 40

9. FLUTTER – GESTIÓN DEL ESTADO .................................................. .......................................... 45

Gestión de estados efímeros ............................................... .................................................... .....................45

Estado de la aplicación: modelo_de_ámbito ............................................... .................................................... ..........57

Navegación y enrutamiento ............................................... .................................................... ..........................68

10. FLUTTER – ANIMACIÓN ............................................... .................................................... .... 82

Introducción................................................. .................................................... ..........................................82

Clases basadas en animación .............................................. .................................................... .............................82

Flujo de trabajo de Flutter Animation................................................ .................................................... ..........83

Aplicación de trabajo .................................................. .................................................... ....................................84

11. FLUTTER: ESCRIBIR CÓDIGO ESPECÍFICO DE ANDROID ........................................... ..................... 93

12. FLUTTER: ESCRIBIENDO EL CÓDIGO ESPECÍFICO DE IOS ........................................... ............................. 100

13. FLUTTER – INTRODUCCIÓN AL PAQUETE ........................................... .......................... 103

Tipos de paquetes ............................................... .................................................... ..........................................103

Uso de un paquete Dart .............................................. .................................................... ....................................104

Desarrollar un paquete de complementos de Flutter.................................... .................................................... ....................104

14. FLUTTER: ACCESO A LA API REST ........................................... ............................................. 114

Conceptos básicos................................................ .................................................... ..........................................114

Acceder a la API de servicio del producto.................................... .................................................... ......................115

iii
Machine Translated by Google

Aleteo

15. FLUTTER – CONCEPTOS DE BASE DE DATOS ........................................... ...................................... 125

SQLite .................................................. .................................................... .................................................... .....125

Tienda de fuego en la nube .................................. .................................................... ..........................................133

16. FLUTTER – INTERNACIONALIZACIÓN ............................................... ............................. 138

Uso del paquete intl .................................................. .................................................... ..........................................143

17. FLUTTER – PRUEBA .................................................. .................................................... ........ 147

Tipos de prueba.................................................... .................................................... ..........................................147

Prueba de widgets................................................. .................................................... ..........................................147

Pasos involucrados .................................................. .................................................... ..........................................148

Ejemplo de trabajo.................................................. .................................................... ..........................................149

18. FLUTTER – DESPLIEGUE ............................................... .................................................. 151

Aplicación de Android .................................................. .................................................... ....................................151

Aplicación para iOS .................................................. .................................................... ..........................................151

19. FLUTTER – HERRAMIENTAS DE DESARROLLO ........................................... .................................... 153

Conjuntos de widgets .............................................. .................................................... .......................................................153

Flutter Desarrollo con Visual Studio Code .................................................. ..........................................................153

Dart DevTools................................................... .................................................... ............................................153

SDK de Flutter................................................. .................................................... ..........................................................155

20. FLUTTER – ESCRITURA DE APLICACIONES AVANZADAS ........................................... .......... 157

21. FLUTTER – CONCLUSIÓN ............................................... .................................................... 180

IV
Machine Translated by Google

1. Aleteo – Introducción

En general, desarrollar una aplicación móvil es una tarea compleja y desafiante. Hay muchos marcos disponibles para
desarrollar una aplicación móvil. Android proporciona un marco nativo basado en el lenguaje Java e iOS proporciona un
marco nativo basado en el lenguaje Objective-C/Shift.

Sin embargo, para desarrollar una aplicación compatible con ambos sistemas operativos, necesitamos codificar en dos
idiomas diferentes usando dos marcos diferentes. Para ayudar a superar esta complejidad, existen marcos móviles que
admiten ambos sistemas operativos. Estos marcos van desde un marco de aplicación móvil híbrido basado en HTML
simple (que usa HTML para la interfaz de usuario y JavaScript para la lógica de la aplicación) hasta un marco específico
de lenguaje complejo (que hace el trabajo pesado de convertir el código en código nativo). Independientemente de su
simplicidad o complejidad, estos marcos siempre tienen muchas desventajas, siendo uno de los principales inconvenientes
su lento rendimiento.

En este escenario, Flutter, un marco simple y de alto rendimiento basado en el lenguaje Dart, proporciona un alto
rendimiento al representar la interfaz de usuario directamente en el lienzo del sistema operativo en lugar de a través del
marco nativo.

Flutter también ofrece muchos widgets (UI) listos para usar para crear una aplicación moderna. Estos
los widgets están optimizados para el entorno móvil y diseñar la aplicación usando widgets es tan simple como diseñar
HTML.

Para ser específicos, la aplicación Flutter es en sí misma un widget. Los widgets de Flutter también admiten animaciones y
gestos. La lógica de la aplicación se basa en la programación reactiva. El widget puede tener opcionalmente un estado. Al
cambiar el estado del widget, Flutter comparará automáticamente (programación reactiva) el estado del widget (antiguo y
nuevo) y renderizará el widget solo con los cambios necesarios en lugar de volver a renderizar todo el widget.

Discutiremos la arquitectura completa en los próximos capítulos.

Características de Flutter

Flutter framework ofrece las siguientes características a los desarrolladores:

• Marco moderno y reactivo.

• Utiliza el lenguaje de programación Dart y es muy fácil de aprender.


• Desarrollo rápido.
• Interfaces de usuario hermosas y fluidas.

• Enorme catálogo de widgets.

• Ejecuta la misma interfaz de usuario para varias plataformas.

• Aplicación de alto rendimiento.

1
Machine Translated by Google

Aleteo

Ventajas de Flutter
Flutter viene con widgets hermosos y personalizables para un alto rendimiento y una aplicación móvil
sobresaliente. Cumple con todas las necesidades y requisitos personalizados. Además de estos, Flutter ofrece
muchas más ventajas, como se menciona a continuación:
• Dart tiene un gran depósito de paquetes de software que le permite ampliar las capacidades de su
aplicación.

• Los desarrolladores deben escribir una única base de código para ambas aplicaciones (tanto en plataformas
Android como iOS). Es posible que Flutter también se extienda a otras plataformas en el futuro.

• Flutter necesita menos pruebas. Debido a su base de código único, es suficiente si escribimos pruebas
automatizadas una vez para ambas plataformas.

• La simplicidad de Flutter lo convierte en un buen candidato para un desarrollo rápido. Su capacidad de


personalización y extensibilidad lo hace aún más poderoso.

• Con Flutter, los desarrolladores tienen control total sobre los widgets y su diseño.

• Flutter ofrece excelentes herramientas para desarrolladores, con una increíble recarga en caliente.

Desventajas de Flutter
A pesar de sus muchas ventajas, flutter tiene los siguientes inconvenientes:

• Dado que está codificado en lenguaje Dart, un desarrollador necesita aprender un nuevo lenguaje (aunque es
fácil de aprender).

• El marco moderno intenta separar la lógica y la interfaz de usuario tanto como sea posible pero, en Flutter, la
interfaz de usuario y la lógica se entremezclan. Podemos superar esto usando codificación inteligente y
usando un módulo de alto nivel para separar la interfaz de usuario y la lógica.

• Flutter es otro marco para crear aplicaciones móviles. Los desarrolladores tienen dificultades para elegir
las herramientas de desarrollo adecuadas en un segmento muy poblado.

2
Machine Translated by Google

Aleteo
2. Flutter – Instalación

Este capítulo lo guiará a través de la instalación de Flutter en su computadora local en detalle.

Instalación en Windows
En esta sección, veamos cómo instalar Flutter SDK y sus requisitos en un sistema Windows.

Paso 1: Vaya a la URL, https://flutter.dev/docs/get-started/install/windows y descargue el SDK de Flutter más


reciente. A partir de abril de 2019, la versión es 1.2.1 y el archivo es flutter_windows_v1.2.1-stable.zip.

Paso 2: Descomprima el archivo zip en una carpeta, diga C:\flutter\

Paso 3: actualice la ruta del sistema para incluir el directorio flutter bin.

Paso 4: Flutter proporciona una herramienta, flutter doctor para verificar que se cumplan todos los requisitos de
desarrollo de flutter.

médico aleteo

Paso 5: ejecutar el comando anterior analizará el sistema y mostrará su informe como se muestra a
continuación:

Resumen médico (para ver todos los detalles, ejecute flutter doctor -v):
[ÿ] Flutter (Canal estable, v1.2.1, en Microsoft Windows [Versión 10.0.17134.706],
configuración regional en-US)
[ÿ] Cadena de herramientas de Android: desarrollo para dispositivos Android (Android SDK versión
28.0.3)
[ÿ] Android Studio (versión 3.2)
[ÿ] VS Code, edición de 64 bits (versión 1.29.1)
[!] Dispositivo conectado
! No hay dispositivos disponibles

! El médico encontró problemas en 1 categoría.

El informe dice que todas las herramientas de desarrollo están disponibles pero el dispositivo no está conectado.
Podemos solucionar esto conectando un dispositivo Android a través de USB o iniciando un emulador de
Android.

Paso 6: instale el último SDK de Android, si lo informa flutter doctor

Paso 7: instale la última versión de Android Studio, si lo informa flutter doctor

Paso 8: Inicie un emulador de Android o conecte un dispositivo Android real al sistema.

Paso 9: Instale el complemento Flutter and Dart para Android Studio. Proporciona una plantilla de inicio para
crear una nueva aplicación Flutter, una opción para ejecutar y depurar la aplicación Flutter en el propio estudio de
Android, etc.

3
Machine Translated by Google

Aleteo

• Abra Android Studio.


• Haga clic en Archivo > Configuración > Complementos.

• Seleccione el complemento Flutter y haga clic en Instalar.


• Haga clic en Sí cuando se le solicite instalar el complemento Dart.
• Reinicie el estudio de Android.

Instalación en macOS
Para instalar Flutter en MacOS, tendrás que seguir los siguientes pasos:

Paso 1: Vaya a la URL, https://flutter.dev/docs/get-started/install/macos y descargue el último SDK de Flutter. A partir


de abril de 2019, la versión es 1.2.1 y el archivo es flutter_macos_v1.2.1-
estable.zip.

Paso 2: Descomprima el archivo zip en una carpeta, diga /ruta/a/flutter

Paso 3: actualice la ruta del sistema para incluir el directorio flutter bin (en el archivo ~/.bashrc).

> export PATH="$PATH:/path/to/flutter/bin"

Paso 4: habilite la ruta actualizada en la sesión actual usando el siguiente comando y luego verifíquelo también.

fuente ~/.bashrc
fuente $HOME/.bash_profile echo
$RUTA

Flutter proporciona una herramienta, flutter doctor para verificar que se cumplan todos los requisitos del desarrollo de
flutter. Es similar a la contraparte de Windows.

Paso 5: instale el XCode más reciente, si lo informa flutter doctor

Paso 6: instale el último SDK de Android, si lo informa flutter doctor

Paso 7: instale la última versión de Android Studio, si lo informa flutter doctor

Paso 8: Inicie un emulador de Android o conecte un dispositivo Android real al sistema para desarrollar una aplicación
de Android.

Paso 9: Abra el simulador de iOS o conecte un dispositivo iPhone real al sistema para desarrollar la aplicación iOS.

Paso 10: Instale el complemento Flutter and Dart para Android Studio. Proporciona la plantilla de inicio para crear una
nueva aplicación Flutter, la opción de ejecutar y depurar la aplicación Flutter en el propio estudio de Android, etc.

• Abra Android Studio.


• Haga clic en Preferencias > Complementos.
• Seleccione el complemento Flutter y haga clic en Instalar.
• Haga clic en Sí cuando se le solicite instalar el complemento Dart.
• Reinicie el estudio de Android.

4
Machine Translated by Google

Aleteo
3. Flutter: creación de una aplicación
sencilla en Android Studio

En este capítulo, vamos a crear una aplicación Flutter simple para comprender los conceptos básicos de
la creación de una aplicación Flutter en Android Studio.

Paso 1: abre Android Studio

Paso 2: Crea un Proyecto Flutter. Para esto, haga clic en Archivo -> Nuevo -> Nuevo proyecto Flutter

5
Machine Translated by Google

Aleteo

Paso 3: seleccione la aplicación Flutter. Para ello, seleccione Aplicación Flutter y haga clic en Siguiente.

Paso 4: Configure la aplicación como se muestra a continuación y haga clic en Siguiente.

• Nombre del proyecto: hello_app


• Ruta del SDK de Flutter: <path_to_flutter_sdk>
• Ubicación del proyecto: <ruta_a_la_carpeta_del_proyecto>
• Descripción: Aplicación hello world basada en Flutter

6
Machine Translated by Google

Aleteo

Paso 5: Configurar Proyecto.

Establezca el dominio de la empresa como flutterapp.tutorialspoint.com y haga clic en Finalizar

Paso 6: Ingrese el dominio de la empresa.

Android Studio crea una aplicación flutter completamente funcional con una funcionalidad mínima.
Verifiquemos la estructura de la aplicación y luego, cambiemos el código para hacer nuestra tarea.

7
Machine Translated by Google

Aleteo

La estructura de la aplicación y su finalidad es la siguiente:

Aquí se explican varios componentes de la estructura de la aplicación:

• Android : código fuente generado automáticamente para crear una aplicación de Android
• ios : código fuente generado automáticamente para crear una aplicación ios
• lib - Carpeta principal que contiene código Dart escrito usando flutter framework

• lib/main.dart - Punto de entrada de la aplicación Flutter

• test - Carpeta que contiene el código Dart para probar la aplicación flutter

• test/widget_test.dart - Código de muestra

• .gitignore : archivo de control de versiones de Git

• .metadata : generado automáticamente por las herramientas flutter

• .packages : generado automáticamente para rastrear los paquetes flutter


• .iml : archivo de proyecto utilizado por el estudio de Android

• pubspec.yaml : utilizado por Pub, administrador de paquetes Flutter

• pubspec.lock : generado automáticamente por el administrador de paquetes de Flutter, Pub

• README.md : archivo de descripción del proyecto escrito en formato Markdown

8
Machine Translated by Google

Aleteo

Paso 7: Reemplace el código dart en el archivo lib/ main.dart con el siguiente código:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

devolver MaterialApp(
title: 'Aplicación de demostración Hello World',
theme: ThemeData( PrimarySwatch: Colors.blue, ),
home: MyHomePage(title: 'Home page'), );

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
Text( 'Hello World',

), );
}
}

Entendamos el código dart línea por línea.

• Línea 1: importa el paquete flutter, material. El material es un paquete flutter para crear una
interfaz de usuario de acuerdo con las pautas de diseño de materiales especificadas por
Android.

• Línea 3: Este es el punto de entrada de la aplicación Flutter. Llama a la función runApp y le pasa
un objeto de la clase MyApp . El propósito de la función runApp es adjuntar el widget dado a
la pantalla.

• Línea 5 - 17: el widget se usa para crear una interfaz de usuario en el marco flutter.
StatelessWidget es un widget que no mantiene ningún estado del widget. MyApp amplía
StatelessWidget y anula su método de compilación . El propósito de la construcción

9
Machine Translated by Google

Aleteo

método es crear una parte de la interfaz de usuario de la aplicación. Aquí, el método de


compilación usa MaterialApp, un widget para crear la interfaz de usuario de nivel raíz de la
aplicación. Tiene tres propiedades: título, tema y hogar.

o title es el título de la solicitud.

o tema es el tema del widget. Aquí, establecemos el azul como el color general de la
aplicación utilizando la clase ThemeData y su propiedad, primarySwatch.

o home es la interfaz de usuario interna de la aplicación, en la que configuramos otro widget,


Mi página de inicio

• Línea 19 - 38: MyHomePage es igual que MyApp excepto que devuelve Scaffold
Widget. Scaffold es un widget de nivel superior junto al widget MaterialApp que se utiliza
para crear un diseño de material conforme a la interfaz de usuario. Tiene dos propiedades
importantes, appBar para mostrar el encabezado de la aplicación y el cuerpo para mostrar
el contenido real de la aplicación. AppBar es otro widget para representar el encabezado de
la aplicación y lo hemos usado en la propiedad appBar . En la propiedad del cuerpo , hemos
utilizado el widget Center , que centra su widget secundario. El texto es el widget final y más
interno para mostrar el texto y se muestra en el centro de la
pantalla.

Paso 8: Ahora, ejecute la aplicación usando Ejecutar -> Ejecutar main.dart

10
Machine Translated by Google

Aleteo

Paso 9: Finalmente, la salida de la aplicación es la siguiente:

11
Machine Translated by Google

4. Flutter: arquitectura de la aplicación Flutter Aleteo

En este capítulo, analicemos la arquitectura del framework Flutter.

Widgets
El concepto central del marco Flutter es En Flutter, todo es un widget. Los widgets son básicamente
componentes de la interfaz de usuario que se utilizan para crear la interfaz de usuario de la aplicación.

En Flutter, la aplicación es en sí misma un widget. La aplicación es el widget de nivel superior y su interfaz


de usuario se construye usando uno o más hijos (widgets), que nuevamente se construyen usando sus
hijos widgets. Esta función de composición nos ayuda a crear una interfaz de usuario de cualquier complejidad.

Por ejemplo, la jerarquía de widgets de la aplicación hello world (creada en el capítulo anterior) se especifica
en el siguiente diagrama:

Aquí vale la pena destacar los siguientes puntos:

12
Machine Translated by Google

Aleteo

• MyApp es el widget creado por el usuario y se construye usando el widget nativo de Flutter,
MaterialApp.

• MaterialApp tiene una propiedad de inicio para especificar la interfaz de usuario de la página de inicio,
que es nuevamente un widget creado por el usuario, MyHomePage.

• MyHomePage se construye utilizando otro widget nativo de flutter, Scaffold.

• Scaffold tiene dos propiedades: body y appBar.

• body se usa para especificar su interfaz de usuario principal y appBar se usa para especificar su encabezado
interfaz de usuario.

• La interfaz de usuario del encabezado se crea con el widget nativo de flutter, la barra de aplicaciones y la interfaz de usuario del cuerpo se crean con
Widget central .

• El widget Center tiene una propiedad, Child, que hace referencia al contenido real y se construye usando el widget
Text .

Gestos
Los widgets de Flutter admiten la interacción a través de un widget especial, GestureDetector.
GestureDetector es un widget invisible que tiene la capacidad de capturar las interacciones del usuario, como tocar,
arrastrar, etc., de su widget secundario. Muchos widgets nativos de Flutter admiten la interacción mediante el uso de
GestureDetector. También podemos incorporar funciones interactivas en el widget existente componiéndolo con el
widget GestureDetector . Aprenderemos los gestos por separado en los próximos capítulos.

Concepto de Estado
Los widgets de Flutter admiten el mantenimiento del estado al proporcionar un widget especial, StatefulWidget.
El widget debe derivarse del widget StatefulWidget para admitir el mantenimiento del estado y todos los demás widgets
deben derivarse de StatelessWidget. Los widgets de Flutter son reactivos en nativo. Esto es similar a reactjs y
StatefulWidget se renderizará automáticamente cada vez que se cambie su estado interno. La nueva representación se
optimiza encontrando la diferencia entre la interfaz de usuario del widget antiguo y el nuevo y presentando solo los
cambios necesarios.

Capas
El concepto más importante del marco Flutter es que el marco se agrupa en múltiples categorías en términos de
complejidad y se organiza claramente en capas de complejidad decreciente. Una capa se crea utilizando su capa de
nivel inmediatamente siguiente. La capa superior es un widget específico para Android e iOS. La siguiente capa tiene
todos los widgets nativos de flutter. La siguiente capa es la capa de renderizado , que es un componente de renderizado
de bajo nivel y renderiza todo en la aplicación flutter. Las capas se reducen al código específico de la plataforma central.

13
Machine Translated by Google

Aleteo

La descripción general de una capa en Flutter se especifica en el siguiente diagrama:

Los siguientes puntos resumen la arquitectura de Flutter:


• En Flutter, todo es un widget y un widget complejo se compone de widgets ya existentes.

• Las funciones interactivas se pueden incorporar cuando sea necesario usando GestureDetector
artilugio.

• El estado de un widget se puede mantener siempre que sea necesario usando StatefulWidget
artilugio.

• Flutter ofrece un diseño en capas para que cualquier capa se pueda programar según la complejidad
de la tarea.

Discutiremos todos estos conceptos en detalle en los próximos capítulos.

14
Machine Translated by Google

Aleteo
5. Flutter – Introducción a la programación de dardos

Dart es un lenguaje de programación de propósito general de código abierto. Es desarrollado originalmente


por Google. Dart es un lenguaje orientado a objetos con sintaxis de estilo C. Admite conceptos de programación
como interfaces, clases, a diferencia de otros lenguajes de programación, Dart no admite matrices. Las
colecciones de Dart se pueden usar para replicar estructuras de datos como matrices, genéricos y tipos
opcionales.

El siguiente código muestra un programa Dart simple:

vacío principal()
{
print("El lenguaje Dart es fácil de aprender");
}

Variables y tipos de datos


La variable se denomina ubicación de almacenamiento y los tipos de datos simplemente se refieren al tipo y
tamaño de los datos asociados con variables y funciones.

Dart usa la palabra clave var para declarar la variable. La sintaxis de var se define a continuación,

var nombre = 'Dardo';

Las palabras clave final y const se utilizan para declarar constantes. Se definen como sigue:

vacío principal ()
{ final a = 12;
constante pi = 3,14;
imprimir (a); imprimir
(pi);
}

El lenguaje Dart admite los siguientes tipos de datos:

• Números: se utiliza para representar literales numéricos: enteros y dobles.

• Strings: Representa una secuencia de caracteres. Los valores de cadena se especifican entre comillas
simples o dobles.

• Booleanos: Dart usa la palabra clave bool para representar valores booleanos: verdadero y falso.

• Listas y Mapas: Se utiliza para representar una colección de objetos. Una lista simple se puede definir
de la siguiente manera:

void main() { var


lista = [1,2,3,4,5]; imprimir
(lista);
}

15
Machine Translated by Google

Aleteo

La lista que se muestra arriba produce la lista [1,2,3,4,5].

El mapa se puede definir como se muestra aquí:

void main() { var


mapeo = {'id': 1,'name':'Dart'}; imprimir (mapeo);

• Dinámica: si el tipo de variable no está definido, su tipo predeterminado es dinámico. El


El siguiente ejemplo ilustra la variable de tipo dinámico:

void main()
{ nombre dinámico = "Dart";
imprimir (nombre);
}

Toma de decisiones y bucles


Un bloque de toma de decisiones evalúa una condición antes de que se ejecuten las instrucciones. Dart
admite declaraciones If, If..else y switch.

Los bucles se utilizan para repetir un bloque de código hasta que se cumpla una condición específica. Soportes para dardos
para, para..en , while y do..while bucles.

Entendamos un ejemplo simple sobre el uso de sentencias de control y bucles:

void main()
{ for( var i = 1 ; i <= 10; i++ ) { if(i%2==0)

{
imprimir (yo);
}
}
}

El código anterior imprime los números pares del 1 al 10.

Funciones
Una función es un grupo de declaraciones que juntas realizan una tarea específica. echemos un vistazo a
una función simple en Dart como se muestra aquí:

vacío principal ()
{ añadir (3,4);
}

añadir vacío (int a, int b) { int


c;
c=a+b;
imprimir (c);
}

dieciséis
Machine Translated by Google

Aleteo

La función anterior suma dos valores y produce 7 como salida.

Programación orientada a objetos


Dart es un lenguaje orientado a objetos. Admite características de programación orientada a objetos como clases, interfaces,
etc.

Una clase es un modelo para crear objetos. Una definición de clase incluye lo siguiente:

• Campos

• Getters y setters

• Constructores

• Funciones

Ahora, creemos una clase simple usando las definiciones anteriores:

clase Empleado {
Nombre de cadena;

//método captador
String get emp_name { nombre
de retorno;
}

//método setter
void set emp_name(String nombre) { this.name
= nombre;
}

//definicion de la funcion
resultado nulo()
{
imprimir (nombre);
}

} void main() { //
creación de objetos
Empleado emp = nuevo Empleado();
emp.nombre="empleado1";
emp.resultado(); //Llamada de función
}

17
Machine Translated by Google

Aleteo
6. Flutter – Introducción a los widgets

Como aprendimos en el capítulo anterior, los widgets lo son todo en el marco de trabajo de Flutter. Ya hemos
aprendido cómo crear nuevos widgets en capítulos anteriores.

En este capítulo, comprendamos el concepto real detrás de la creación de widgets y los diferentes tipos de
widgets disponibles en el marco Flutter .

Veamos el widget MyHomePage de la aplicación Hello World . El código para este propósito es el siguiente:

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@anular
Compilación del widget (contexto BuildContext) {
andamio de vuelta(
barra de aplicaciones: barra de aplicaciones (

título: Texto(este.título),
),
cuerpo: Centro(
niño: Texto(
'Hola Mundo',
)),
);
}
}

Aquí, hemos creado un nuevo widget extendiendo StatelessWidget.

Tenga en cuenta que StatelessWidget solo requiere que se implemente una única compilación de método en
su clase derivada. El método de construcción obtiene el entorno de contexto necesario para construir los
widgets a través del parámetro BuildContext y devuelve el widget que construye.

En el código, hemos usado el título como uno de los argumentos del constructor y también usamos la clave
como otro argumento. El título se usa para mostrar el título y la clave se usa para identificar el widget en el
entorno de construcción.

Aquí, el método de construcción llama al método de construcción de Scaffold , que a su vez llama al método de construcción
método de AppBar y Center para construir su interfaz de usuario.

Finalmente, el método de compilación Center llama al método de compilación Text .

18
Machine Translated by Google

Aleteo

Para una mejor comprensión se da a continuación la representación visual del mismo:

Visualización de creación de widgets

En Flutter, los widgets se pueden agrupar en varias categorías según sus características, como se indica a continuación:

• Widgets específicos de la plataforma

• Widgets de diseño

• Widgets de mantenimiento de estado

• Widgets básicos/independientes de la plataforma

Vamos a discutir cada uno de ellos en detalle ahora.

Widgets específicos de la plataforma

Flutter tiene widgets específicos para una plataforma en particular: Android o iOS.

Los widgets específicos de Android están diseñados de acuerdo con las pautas de diseño de materiales del sistema
operativo Android. Los widgets específicos de Android se denominan widgets de material.

Los widgets específicos de iOS están diseñados de acuerdo con las Pautas de interfaz humana de Apple y se denominan
widgets de Cupertino .

Algunos de los widgets de material más utilizados son los siguientes:

• Andamio

• Barra de aplicaciones

19
Machine Translated by Google

Aleteo

• Barra de navegación inferior

• Barra de pestañas

• TabBarView

Azulejo de lista

• Botón elevado

• Botón de acción flotante


• Botón Plano

• Botón de icono

• Botón desplegable

• Botón de menú emergente

• Barra de botones

• Campo de texto

• Casilla de verificación

• Radio

• Cambiar

• Control deslizante

• Selectores de fecha y hora

• SimpleDialog •

AlertDialog

Algunos de los widgets de Cupertino más utilizados son los siguientes:

• Botón de Cupertino

• Selector de Cupertino

• CupertinoDatePicker •

CupertinoTimerPicker

• Barra de navegación de Cupertino

• Barra de pestañas de Cupertino

• CupertinoTabScaffold •

CupertinoTabView •

CupertinoTextField

• Diálogo de Cupertino

• CupertinoDialogAction

• CupertinoFullscreenDialogTransition •

CupertinoPageScaffold

• Transición de la página de Cupertino

• Hoja de acción de Cupertino

• CupertinoActivityIndicator •

CupertinoAlertDialog •

CupertinoPopupSurface

20
Machine Translated by Google

Aleteo

• CupertinoSlider

Widgets de diseño

En Flutter, se puede crear un widget al componer uno o más widgets. Para componer múltiples widgets en un
solo widget, Flutter proporciona una gran cantidad de widgets con función de diseño. Por ejemplo, el widget
secundario se puede centrar usando el widget Center .

Algunos de los widgets de diseño populares son los siguientes:

• Contenedor: Una caja rectangular decorada con widgets BoxDecoration con


fondo, borde y sombra.

• Centro: Centre su widget secundario

• Fila: Ordena sus hijos en sentido horizontal.

• Columna: Ordena sus hijos en la dirección vertical.

• Apilar: Disponer uno encima del otro.

Revisaremos los widgets de diseño en detalle en la próxima Introducción a los widgets de diseño.
capítulo.

Widgets de mantenimiento de estado

En Flutter, todos los widgets se derivan de StatelessWidget o StatefulWidget.

El widget derivado de StatelessWidget no tiene información de estado, pero puede contener un widget derivado
de StatefulWidget. La naturaleza dinámica de la aplicación es a través del comportamiento interactivo de los
widgets y los cambios de estado durante la interacción. Por ejemplo, tocar un botón de contador aumentará/
disminuirá el estado interno del contador en uno y la naturaleza reactiva del widget Flutter volverá a renderizar
automáticamente el widget utilizando la nueva información de estado.

Aprenderemos el concepto de widgets StatefulWidget en detalle en el próximo capítulo de administración de


estado .

Widgets básicos/independientes de la plataforma

Flutter proporciona una gran cantidad de widgets básicos para crear una interfaz de usuario simple y compleja
de manera independiente a la plataforma. Veamos algunos de los widgets básicos en este capítulo.

Texto

El widget de texto se utiliza para mostrar un trozo de cadena. El estilo de la cadena se puede establecer
mediante la propiedad de estilo y la clase TextStyle . El código de muestra para este propósito es el siguiente:

Texto('¡Hola mundo!', estilo: TextStyle(fontWeight: FontWeight.bold))

El widget de texto tiene un constructor especial, Text.rich, que acepta el elemento secundario de tipo TextSpan
para especificar la cadena con un estilo diferente. El widget TextSpan es de naturaleza recursiva y acepta
TextSpan como sus elementos secundarios. El código de muestra para este propósito es el siguiente:

21
Machine Translated by Google

Aleteo

Text.rich( TextSpan( children:


<TextSpan>[ TextSpan(text: "Hola ", estilo: TextStyle(fontStyle:
FontStyle.cursiva)),
TextSpan(texto: "Mundo", estilo: TextStyle(fontWeight: FontWeight.bold)), ], ),

Las propiedades más importantes del widget de texto son las siguientes:

• maxLines, int: Número máximo de líneas a mostrar

• desbordamiento, TextOverFlow: especifique cómo se maneja el desbordamiento visual usando


clase TextOverFlow

• estilo, TextStyle: especifica el estilo de la cadena usando la clase TextStyle

• textAlign, TextAlign: Alineación del texto como derecha, izquierda, justificar, etc., usando
clase TextAlign

• textDirection, TextDirection: dirección del flujo de texto, ya sea de izquierda a derecha o de derecha
a la izquierda

Imagen

El widget de imagen se utiliza para mostrar una imagen en la aplicación. El widget de imagen proporciona
diferentes constructores para cargar imágenes de múltiples fuentes y son los siguientes:

• Imagen: cargador de imágenes genéricas mediante ImageProvider

• Image.asset: carga la imagen desde los activos del proyecto flutter

• Image.file: carga la imagen desde la carpeta del sistema.

• Image.memory: carga la imagen desde la memoria. •

Image.Network: carga la imagen desde la red.

La opción más sencilla para cargar y mostrar una imagen en Flutter es incluir la imagen como activos de la
aplicación y cargarla en el widget a pedido.

• Crear una carpeta, activos en la carpeta del proyecto y colocar las imágenes necesarias.

• Especifique los activos en pubspec.yaml como se muestra a continuación:

aleteo:
activos:
- activos/smiley.png

• Ahora, cargue y muestre la imagen en la aplicación.

Imagen.activo('activos/sonrisa.png')

22
Machine Translated by Google

Aleteo

• El código fuente completo del widget MyHomePage de la aplicación hello world y


el resultado es el que se muestra a continuación:

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
Image.asset("assets/smiley.png")

), );
}

La imagen cargada es como se muestra a continuación:

23
Machine Translated by Google

Aleteo

Las propiedades más importantes del widget Imagen son las siguientes:

• imagen, ImageProvider: Imagen real para cargar •


ancho, doble - Ancho de la imagen
• altura, doble - Altura de la imagen
• alineación, AlignmentGeometry: cómo alinear la imagen dentro de sus límites

Icono

El widget de icono se usa para mostrar un glifo de una fuente descrita en la clase IconData . El código para cargar
un icono de correo electrónico simple es el siguiente:

Icono(Iconos.email)

El código fuente completo para aplicarlo en la aplicación hello world es el siguiente:

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
Icon(Icons.email)

), );
}
}

24
Machine Translated by Google

Aleteo

El icono cargado es como se muestra a continuación:

25
Machine Translated by Google

Aleteo
7. Flutter – Introducción a los diseños

Dado que el concepto central de Flutter es que todo es un widget, Flutter incorpora una funcionalidad de diseño
de interfaz de usuario en los propios widgets. Flutter proporciona una gran cantidad de widgets especialmente
diseñados como Container, Center, Align, etc., solo con el propósito de diseñar la interfaz de usuario. Los
widgets se construyen al componer otros widgets que normalmente usan widgets de diseño. Vamos a aprender
el concepto de diseño de Flutter en este capítulo.

Tipo de widgets de diseño


Los widgets de diseño se pueden agrupar en dos categorías distintas en función de su elemento secundario:

• Widget de apoyo a un solo niño


• Widget compatible con varios niños

Aprendamos tanto el tipo de widgets como su funcionalidad en las próximas secciones.

Widgets para un solo hijo


En esta categoría, los widgets tendrán solo un widget como hijo y cada widget tendrá una funcionalidad de
diseño especial.

Por ejemplo, el widget Center simplemente centra su widget secundario con respecto a su widget principal y el
widget Container proporciona una flexibilidad completa para colocarlo en cualquier lugar dentro de él usando
diferentes opciones como relleno, decoración, etc.

Los widgets de un solo hijo son excelentes opciones para crear widgets de alta calidad que tengan una
funcionalidad única, como un botón, una etiqueta, etc.

El código para crear un botón simple usando el widget Container es el siguiente:

class MyButton extiende StatelessWidget {


MiBotón({Clave clave}) : super(clave: clave);

@anular
Compilación del widget (contexto BuildContext) {
contenedor de retorno(
decoración: const BoxDecoration(
borde: borde (
arriba: BorderSide (ancho: 1.0, color: Color (0xFFFFFFFFFF)),
izquierda: BorderSide (ancho: 1.0, color: Color (0xFFFFFFFFFF)),
derecha: BorderSide (ancho: 1.0, color: Color (0xFFFF000000)),
inferior: BorderSide (ancho: 1.0, color: Color (0xFFFF000000)),
),
),
niño: contenedor (
relleno: const EdgeInsets.simétrico (horizontal: 20,0, vertical: 2,0),
decoración: const BoxDecoration(
borde: borde (
arriba: BorderSide (ancho: 1.0, color: Color (0xFFFFDFDFDF)),

26
Machine Translated by Google

Aleteo

izquierda: BorderSide (ancho: 1.0, color: Color (0xFFFFDFDFDF)),


derecha: BorderSide (ancho: 1.0, color: Color (0xFFFF7F7F7F)),
inferior: BorderSide (ancho: 1.0, color: Color (0xFFFF7F7F7F)),
),
color: colores.gris,
),
hijo: const Texto('OK',
textAlign: TextAlign.center, estilo: TextStyle(color:
Colores.negro)),
),
);
}
}

Aquí, hemos utilizado dos widgets: un widget de contenedor y un widget de texto . El resultado del widget es
un botón personalizado como se muestra a continuación:

Veamos algunos de los widgets de diseño de un solo hijo más importantes proporcionados por Flutter:

• Relleno: Se utiliza para organizar su widget hijo por el relleno dado. Aquí, la clase EdgeInsets puede
proporcionar relleno .

• Alinear: alinea su widget secundario dentro de sí mismo usando el valor de la propiedad de alineación . El
valor de la propiedad de alineación puede ser proporcionado por la clase FractionalOffset . La clase
FractionalOffset especifica los desplazamientos en términos de distancia desde la parte superior izquierda.

Algunos de los posibles valores de las compensaciones son los siguientes:

• FractionalOffset(1.0, 0.0) representa la parte superior derecha.


• FractionalOffset(0.0, 1.0) representa la parte inferior izquierda.

• A continuación se muestra un código de ejemplo sobre las compensaciones:

Centrar(
niño: contenedor (
altura: 100.0,
ancho: 100.0,
color: Colores.amarillo,
niño: alinear (
alineación: Compensación fraccional (0.2, 0.6),
niño: contenedor (
altura: 40.0,
ancho: 40.0,
color: colores.rojo,
),
),
),
)

• FittedBox: escala el widget secundario y luego lo posiciona de acuerdo con el ajuste especificado.

• Relación de aspecto: intenta ajustar el tamaño del widget secundario a la relación de aspecto especificada

27
Machine Translated by Google

Aleteo

• Caja restringida

• Base

• FractinalySizedBox

• Altura intrínseca

• Ancho intrínseco

• AdhesivosCaja

• Fuera del escenario

• Caja de desbordamiento

• Caja de tamaño

• SizedOverflowBox

• Transformar

• Custom Single ChildLayout

Nuestra aplicación hello world utiliza widgets de diseño basados en materiales para diseñar la página de inicio.
Modifiquemos nuestra aplicación hello world para construir la página de inicio utilizando widgets de diseño básicos
como se especifica a continuación:

• Contenedor: Widget de contenedor genérico, hijo único, basado en cuadro con alineación, relleno, borde
y margen junto con ricas características de estilo.

• Centro: Widget de contenedor secundario simple y único, que centra su widget secundario.

El código modificado del widget MyHomePage y MyApp es el siguiente:

class MyApp extiende StatelessWidget {

@anular

Compilación del widget (contexto BuildContext) {

return MyHomePage(título: "Aplicación de demostración Hello World");

clase MyHomePage extiende StatelessWidget {

MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@anular

Compilación del widget (contexto BuildContext) {

contenedor de retorno(

decoración: BoxDecoración(

28
Machine Translated by Google

Aleteo

color: colores.blanco,

),

relleno: EdgeInsets.all(25),

niño: Centro (niño:

Texto(
'Hola Mundo',

estilo: EstiloTexto(

color: colores.negro,

Espaciado entre letras: 0.5,

tamaño de fuente: 20,

),

DirecciónTexto: DirecciónTexto.ltr,

),

));

Aquí,

• El widget contenedor es el widget raíz o de nivel superior. El contenedor se configura utilizando la propiedad
de decoración y relleno para diseñar su contenido.

• BoxDecoration tiene muchas propiedades como color, borde, etc., para decorar el Contenedor
widget y aquí, el color se usa para establecer el color del contenedor.

• El relleno del widget Contenedor se establece mediante la clase dgeInsets , que ofrece la opción de especificar
el valor de relleno.

• Centro es el widget secundario del widget Contenedor . Nuevamente, Text es el elemento secundario del
widget Center . El texto se usa para mostrar el mensaje y el Centro se usa para centrar el mensaje de
texto con respecto al widget principal, Contenedor.

29
Machine Translated by Google

Aleteo

El resultado final del código anterior es un ejemplo de diseño como se muestra a continuación:

Múltiples widgets para niños

En esta categoría, un widget determinado tendrá más de un widget secundario y el diseño de cada widget es único.

Por ejemplo, el widget de Fila permite la disposición de sus elementos secundarios en dirección horizontal, mientras
que el widget de Columna permite la disposición de sus elementos secundarios en dirección vertical. Al componer
Fila y Columna, se puede construir un widget con cualquier nivel de complejidad.

Aprendamos algunos de los widgets de uso frecuente en esta sección.

• Fila - Permite ordenar sus hijos de manera horizontal.

• Columna - Permite disponer sus hijos de forma vertical.

• ListView: permite organizar sus elementos secundarios como una lista.

• GridView - Permite organizar sus elementos secundarios como galería.

• Expandido: se usa para hacer que los hijos del widget Fila y Columna ocupen el
área máxima posible.

30
Machine Translated by Google

Aleteo

• Tabla: widget basado en tablas.

• Flujo: widget basado en flujo.

• Pila - Widget basado en pila.

Aplicación de diseño avanzado


En esta sección, aprendamos cómo crear una interfaz de usuario compleja de listado de productos con un
diseño personalizado utilizando widgets de diseño de niños únicos y múltiples.

Para ello, siga la secuencia que se indica a continuación:

• Cree una nueva aplicación Flutter en Android Studio, product_layout_app.

• Reemplace el código main.dart con el siguiente código:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product layout
demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@anular
Creación de widgets (contexto BuildContext)
{ return Scaffold (
appBar:
AppBar( título:
Texto(este.título), ), cuerpo:
Centro( niño:

Texto(
'Hola Mundo', )),

);
}
}

31
Machine Translated by Google

Aleteo

• Aquí,

• Hemos creado el widget MyHomePage extendiendo StatelessWidget en lugar del StatefulWidget predeterminado y
luego eliminamos el código relevante.

• Ahora, cree un nuevo widget, ProductBox según el diseño especificado como se muestra
debajo:

• El código para ProductBox es el siguiente:

class ProductBox extiende StatelessWidget { ProductBox({Clave


clave, este.nombre, esta.descripción, este.precio, esta.imagen})
: super(clave: clave);

nombre final de la
cadena; descripción final de la
cadena; precio internacional final;
imagen de cadena final;

Widget compilación (contexto BuildContext) { return


Container ( relleno: EdgeInsets.all (2), altura: 120,
niño: Tarjeta ( niño: Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + image), Expanded( child:
Container( padding: EdgeInsets.all(5), child: Column( mainAxisAlignment:
MainAxisAlignment.spaceEvenly , children: <Widget>[ Text(this.name, style:
TextStyle(fontWeight: FontWeight.bold)),

Text(this.description), Text("Precio:
" + this.price.toString()), ], )))

]))); }
}

32
Machine Translated by Google

Aleteo

• Tenga en cuenta lo siguiente en el código:

• ProductBox ha utilizado cuatro argumentos como se especifica a continuación:

o nombre - Nombre del producto

o descripción - Descripción del producto

o precio - Precio del producto

o image - Imagen del producto

• ProductBox utiliza siete widgets integrados como se especifica a continuación:

o Contenedor

o Ampliado

o Fila

o Columna

Tarjeta

o Texto

o Imagen

33
Machine Translated by Google

Aleteo

• ProductBox está diseñado utilizando el widget mencionado anteriormente. La disposición o jerarquía


del widget se especifica en el diagrama que se muestra a continuación:

• Ahora, coloque una imagen ficticia (ver a continuación) para obtener información del producto en la carpeta
de activos de la aplicación y configure la carpeta de activos en el archivo pubspec.yaml como se muestra
a continuación:

activos:
- assets/appimages/floppy.png - assets/
appimages/iphone.png - assets/
appimages/laptop.png - assets/
appimages/pendrive.png - assets/
appimages/pixel.png - assets/appimages/
tablet.png

iPhone.png

34
Machine Translated by Google

Aleteo

Píxel.png

portatil.png

tableta.png

Pendrive.png

Disquete.png
• Finalmente, use el widget ProductBox en el widget MyHomePage como se especifica a continuación:

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@anular
Compilación del widget (contexto de
BuildContext) { return Scaffold (appBar:
AppBar (título: Texto ("Lista de productos")), cuerpo: ListView
(shrinkWrap: true,

35
Machine Translated by Google

Aleteo

padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), children:


<Widget>[ ProductBox( name: "iPhone", description: "iPhone is the stylist
phone ever", price: 1000, image: "iphone. png"),

ProductBox( nombre:
"Pixel", descripción: "Pixel es el teléfono con más funciones", precio:
800, imagen: "pixel.png"), ProductBox( nombre: "Laptop", descripción:
"La computadora portátil es la herramienta de desarrollo más productiva
", precio: 2000, imagen: "laptop.png"),

ProductBox( name:
"Tablet", description: "Tablet es el dispositivo más útil para
reunión",
precio: 1500,
imagen: "tablet.png"),

ProductBox( nombre:
"Pendrive", descripción: "Pendrive es un medio de
almacenamiento útil", precio: 100, imagen: "pendrive.png"),
ProductBox(

nombre: "Unidad de
disquete", descripción: "La unidad de disquete es un medio de almacenamiento
de rescate útil", precio: 20, imagen: "floppy.png"),

], ));
}
}

• Aquí, hemos utilizado ProductBox como elementos secundarios del widget ListView .

• El código completo (main.dart) de la aplicación de diseño del producto


(product_layout_app) es el siguiente:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue,

36
Machine Translated by Google

Aleteo

),
home: MyHomePage(título: 'Página de inicio de demostración de diseño de producto'), );

}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title: Text("Product
Listing")), cuerpo: ListView(shrinkWrap: true, padding: const
EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0) , niños:
<Widget>[ ProductBox( nombre: "iPhone", descripción:
"iPhone es el teléfono con más estilo", precio: 1000, imagen: "iphone.png"),

ProductBox( nombre:
"Pixel", descripción: "Pixel es el teléfono con más funciones", precio:
800, imagen: "pixel.png"), ProductBox( nombre: "Laptop", descripción:
"La computadora portátil es la herramienta de desarrollo más productiva
", precio: 2000, imagen: "laptop.png"),

ProductBox( name:
"Tablet", description: "Tablet es el dispositivo más útil para
reunión",
precio: 1500,
imagen: "tablet.png"),

ProductBox( nombre:
"Pendrive", descripción: "Pendrive es un medio de
almacenamiento útil", precio: 100, imagen: "pendrive.png"),
ProductBox(

nombre: "Unidad de
disquete", descripción: "La unidad de disquete es un medio de almacenamiento
de rescate útil", precio: 20, imagen: "floppy.png"),

], ));
}
}

37
Machine Translated by Google

Aleteo

class ProductBox extiende StatelessWidget


{ ProductBox({Clave clave, este.nombre, esta.descripción, este.precio, esta.imagen})
: super(clave: clave);

nombre final de la
cadena; descripción final de la
cadena; precio internacional final;
imagen de cadena final;

Widget compilación (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 120, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, niños:


<Widget>[
Image.asset("assets/appimages/" + image),
Expanded( child: Container( padding:
EdgeInsets.all(5), child:
Column( mainAxisAlignment:
MainAxisAlignment.spaceEvenly, children:
<Widget>[ Text(this.name , estilo: TextStyle(fontWeight:

Peso de la fuente.negrita)),
Text(this.description),
Text("Precio: " + this.price.toString()), ], )))

])));
}
}

38
Machine Translated by Google

Aleteo

El resultado final de la aplicación es el siguiente:

39
Machine Translated by Google

Aleteo
8. Flutter – Introducción a los gestos

Los gestos son principalmente una forma en que un usuario interactúa con una aplicación móvil (o cualquier
dispositivo táctil). Los gestos se definen generalmente como cualquier acción/movimiento físico de un usuario
con la intención de activar un control específico del dispositivo móvil. Los gestos son tan simples como tocar
la pantalla del dispositivo móvil hasta acciones más complejas que se usan en las aplicaciones de juegos.

Algunos de los gestos más utilizados se mencionan aquí:

• Tocar: tocar la superficie del dispositivo con la yema del dedo durante un período corto y luego
soltando la yema del dedo.

• Doble Toque: Tocar dos veces en un corto tiempo.

• Arrastrar: tocar la superficie del dispositivo con la yema del dedo y luego mover la yema del dedo de
manera constante y finalmente soltar la yema del dedo.

• Flick: Similar a arrastrar, pero haciéndolo de una manera más rápida.

• Pellizcar: Pellizcar la superficie del dispositivo con dos dedos.

• Extender/Zoom: Opuesto a pellizcar.

• Panorámica: tocar la superficie del dispositivo con la yema del dedo y moverlo en cualquier dirección
sin soltar la yema del dedo.

Flutter brinda un excelente soporte para todo tipo de gestos a través de su widget exclusivo, GestureDetector.
GestureDetector es un widget no visual que se utiliza principalmente para detectar el gesto del usuario. Para
identificar un gesto dirigido a un widget, el widget se puede colocar dentro del widget GestureDetector.
GestureDetector capturará el gesto y enviará múltiples eventos basados en el gesto.

Algunos de los gestos y los eventos correspondientes se dan a continuación:

Toque

o onTapDown

el onTapUp

o en el toque

el onTapCancelar

• Doble toque

o onDoubleTap

• Pulsación larga

o onLongPress

40
Machine Translated by Google

Aleteo

• Arrastre vertical

o onVerticalDragStart

o onVerticalDragUpdate

o onVerticalDragEnd

• Arrastre horizontal

el onHorizontalDragStart

la actualización onHorizontalDrag

el onHorizontalDragEnd

• Pan

o enPanStart

o onPanUpdate
o enPanEnd

Ahora, modifiquemos la aplicación hello world para incluir la función de detección de gestos y tratemos
de entender el concepto.

• Cambie el contenido del cuerpo del widget MyHomePage como se muestra a continuación:

cuerpo: Centro
(hijo: GestureDetector
(onTap: ()
{ _showDialog (contexto);
},
hijo: Texto(
'Hola Mundo',
)
)
),

• Observe que aquí hemos colocado el widget GestureDetector encima del texto
widget en la jerarquía de widgets, capturó el evento onTap y finalmente mostró una ventana de
diálogo.

• Implemente la función *_showDialog* para presentar un cuadro de diálogo cuando el usuario toque el
mensaje de hola mundo . Utiliza el widget genérico showDialog y AlertDialog para crear un nuevo
widget de diálogo. El código se muestra a continuación:

// función definida por el


usuario void _showDialog(BuildContext context)
{ // función definida flutter showDialog( context:
context, builder: (BuildContext context) { // devolver
objeto de tipo Dialog

41
Machine Translated by Google

Aleteo

return AlertDialog(título:
nuevo texto("Mensaje"), contenido:
nuevo texto("Hola mundo"), acciones:
<Widget>[ new FlatButton( child: nuevo
texto("Cerrar"), onPressed: () { Navigator.
of(contexto).pop(); }, ), ], ); }, ); }

• La aplicación se recargará en el dispositivo utilizando la función Hot Reload . Ahora, simplemente haga
clic en el mensaje Hello World y se mostrará el siguiente cuadro de diálogo:

• Ahora, cierre el cuadro de diálogo haciendo clic en la opción de cerrar en el cuadro de diálogo.

42
Machine Translated by Google

Aleteo

• El código completo (main.dart) es el siguiente:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

devolver MaterialApp(
title: 'Aplicación de demostración Hello World',
theme: ThemeData( PrimarySwatch: Colors.blue, ),
home: MyHomePage(title: 'Home page'), );

}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

// función definida por el usuario


void _showDialog(BuildContext context) { // función
definida flutter showDialog( context: context,
builder: (BuildContext context) { // devolver objeto
de tipo Dialog return AlertDialog( title: new
Text("Message"), contenido: nuevo texto
("Hola mundo"), acciones: <Widget>[ nuevo
FlatButton (hijo: nuevo texto ("Cerrar"), al
presionar: () {

Navegador.de(contexto).pop(); }, ), ], ); }, );

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ),

43
Machine Translated by Google

Aleteo

cuerpo: Centro
(hijo: GestureDetector (onTap:
() { _showDialog
(contexto); }, hijo: Texto (

'Hola Mundo', ))),

);
}
}

Finalmente, Flutter también proporciona un mecanismo de detección de gestos de bajo nivel a través del
widget Listener . Detectará todas las interacciones del usuario y luego enviará los siguientes eventos:

• PointerDown Event
• PointerMoveEvent

• PointerUpEvent
• Puntero Cancelar Evento

Flutter también proporciona un pequeño conjunto de widgets para realizar gestos específicos y avanzados.
Los widgets se enumeran a continuación:

• Desechable: Admite gestos rápidos para descartar el widget.

• Arrastrable: Admite el gesto de arrastrar para mover el widget.

• LongPressDraggable: Admite el gesto de arrastrar para mover un widget, cuando su elemento principal
widget también se puede arrastrar.

• DragTarget: acepta cualquier widget arrastrable .

• IgnorePointer: Oculta el widget y sus elementos secundarios de la detección de gestos


proceso.

• AbsorbPointer: detiene el proceso de detección de gestos en sí mismo y, por lo tanto, cualquier widget
superpuesto tampoco puede participar en el proceso de detección de gestos y, por lo tanto, no se
genera ningún evento.

• Desplazable: Admite el desplazamiento del contenido disponible dentro del widget

44
Machine Translated by Google

Aleteo
9. Flutter – Gestión de estado

Administrar el estado en una aplicación es uno de los procesos más importantes y necesarios en el ciclo de vida de una
aplicación.

Consideremos una simple aplicación de carrito de compras.

• El usuario iniciará sesión con sus credenciales en la aplicación.

• Una vez que el usuario haya iniciado sesión, la aplicación debe conservar los detalles del usuario que inició sesión en todos
la pantalla.

• Una vez más, cuando el usuario selecciona un producto y lo guarda en un carrito, la información del carrito debe
persistir entre las páginas hasta que el usuario haya retirado el carrito.

• La información del usuario y su carrito en cualquier instancia se llama el estado de la aplicación


en esa instancia.

La administración de un estado se puede dividir en dos categorías según la duración del estado particular en una aplicación.

• Efímero: dura unos segundos, como el estado actual de una animación o una sola página, como la calificación actual
de un producto. Flutter lo admite a través de StatefulWidget.

• estado de la aplicación: último para toda la aplicación, como detalles de usuario registrados, información del carrito,
etc., Flutter lo admite a través de scoped_model.

Gestión de estados efímeros


Dado que la aplicación Flutter se compone de widgets, la gestión del estado también se realiza mediante widgets. El punto
de entrada de la gestión del estado es Statefulwidget. El widget se puede heredar de Statefulwidget para mantener su
estado y el estado de sus hijos. Widget con estado
proporciona una opción para que un widget cree un estado, State<T> (donde T es el widget heredado) cuando el widget
se crea por primera vez a través del método createState y luego un método setState para cambiar el estado cuando sea
necesario. El cambio de estado se hará mediante gestos. Por ejemplo, la calificación de un producto se puede cambiar
tocando una estrella en el widget de calificación.

Vamos a crear un widget, RatingBox con mantenimiento de estado. El propósito del widget es mostrar la calificación actual
de un producto específico. El proceso paso a paso para crear un widget de RatingBox con mantenimiento de estado es el
siguiente:

• Crear el widget, RatingBox heredando StatefulWidget

clase RatingBox extiende StatefulWidget {


}

• Crear un estado para RatingBox, _RatingBoxState al heredar State<T>

class _RatingBoxState extiende Estado<RatingBox> {


}

45
Machine Translated by Google

Aleteo

• Anule el método createState of StatefulWidget para crear el estado,


_RatingBoxState

clase RatingBox extiende StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

Cree la interfaz de usuario del widget RatingBox en el método de compilación de _RatingBoxState.


Por lo general, la interfaz de usuario se realizará en el método de compilación del propio widget de RatingBox.
Pero, cuando se necesita mantenimiento de estado, necesitamos construir la interfaz de usuario en el widget
_RatingBoxState. Esto asegura la re-renderización de la interfaz de usuario cada vez que se cambia el estado
del widget.

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max, children:
<Widget>[ Container( padding: EdgeInsets.all(0), child:
IconButton(

icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], iconSize: _size, ), ), Contenedor( relleno:
EdgeInsets.all(0), hijo: IconButton(

icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], iconSize: _size, ), ), Contenedor( relleno:
EdgeInsets.all(0), hijo: IconButton(

icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], iconSize: _size, ), ),

], );
}

46
Machine Translated by Google

Aleteo

Aquí, hemos usado tres estrellas, creadas con el widget IconButton y las hemos organizado usando el widget
Row en una sola fila. La idea es mostrar la calificación a través de la secuencia de estrellas rojas.
Por ejemplo, si la calificación es de dos estrellas, las dos primeras estrellas serán rojas y la última será blanca.

• Escriba métodos en _RatingBoxState para cambiar/establecer el estado del widget.

void _setRatingAsOne()
{ setState( () { _rating = 1; });

void _setRatingAsTwo() {

establecerEstado( ()
{ _puntuación =
2; });
}

void _setRatingAsThree()
{ setState( () { _rating = 3; });

• Aquí, cada método establece la calificación actual del widget a través de setState

• Conecte el gesto del usuario (tocar la estrella) al método de cambio de estado adecuado.

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max, children:
<Widget>[ Container( padding: EdgeInsets.all(0), child:
IconButton(

icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsOne, iconSize:
_size, ), ), Contenedor( relleno: EdgeInsets.all(0), child: IconButton(

icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500],

47
Machine Translated by Google

Aleteo

onPressed: _setRatingAsTwo,
iconSize: _size, ), ),
Container( padding: EdgeInsets.all(0),
child: IconButton(

icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsThree, iconSize:
_size, ), ),

], );
}

Aquí, el evento onPressed llama a la función relevante para cambiar el estado y, posteriormente, cambiar la
interfaz de usuario. Por ejemplo, si un usuario hace clic en la tercera estrella, se llamará a _setRatingAsThree
y cambiará la _rating a 3. Como se cambia el estado, se volverá a llamar al método de compilación y la
interfaz de usuario se compilará y renderizará nuevamente.

• El código completo del widget, RatingBox es el siguiente:

clase RatingBox extiende StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

class _RatingBoxState extiende Estado<RatingBox> {


int _calificación = 0;

void _setRatingAsOne()
{ setState( () { _rating = 1; });

void _setRatingAsTwo() {

establecerEstado( ()
{ _puntuación =
2; });
}

void _setRatingAsThree()
{ setState( () { _rating = 3; });

Compilación del widget (contexto BuildContext) {

48
Machine Translated by Google

Aleteo

doble _tamaño = 20;


imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max, children:
<Widget>[ Container( padding: EdgeInsets.all(0), child:
IconButton(

icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsOne, iconSize:
_size, ), ), Contenedor( relleno: EdgeInsets.all(0), child: IconButton(

icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsTwo, iconSize:
_size, ), ), Contenedor( relleno: EdgeInsets.all(0), child: IconButton(

icon: (_rating >= 3 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsThree, iconSize:
_size, ), ),

], );
}
}

Vamos a crear una nueva aplicación y usar nuestro widget RatingBox recién creado para mostrar la
calificación del producto.

• Cree una nueva aplicación Flutter en Android Studio, product_state_app

Reemplace el código main.dart con el siguiente código:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

49
Machine Translated by Google

Aleteo

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product state
demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:

Texto(
'Hola Mundo', )),

);
}
}

• Aquí,

• Hemos creado el widget MyHomePage extendiendo StatelessWidget en lugar de


StatefulWidget predeterminado y luego eliminó el código relevante.

• Incluir nuestro widget RatingBox recién creado.

• Cree un widget de ProductBox para enumerar el producto junto con la calificación como se especifica a continuación:

class ProductBox extiende StatelessWidget


{ ProductBox({Key key, this.name, this.description, this.price, this.image}) :
super(key: key);

nombre final de la
cadena; descripción final de la
cadena; precio internacional final;
imagen de cadena final;

Widget compilación (contexto BuildContext)


{ return Container (

50
Machine Translated by Google

Aleteo

relleno: EdgeInsets.all(2), altura: 120,


hijo: Tarjeta( hijo: Fila(

mainAxisAlignment: MainAxisAlignment.spaceEvenly, niños: <Widget>[

Image.asset("assets/appimages/" + image), Expanded( child:


Container( padding: EdgeInsets.all(5), child:
Column( mainAxisAlignment:

MainAxisAlignment.spaceEvenly,
children:
<Widget>[ Text(this.name,
style: TextStyle(fontWeight:
Peso de la fuente.negrita)),
Text(this.description), Text("Precio:
" + this.price.toString()),
RatingBox(), ], )))

])));
}
}

• Actualice el widget MyHomePage para incluir el widget ProductBox como se especifica


debajo:

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title: Text("Product
Listing")), cuerpo: ListView(shrinkWrap: true, padding: const
EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0) , niños:
<Widget>[ ProductBox( nombre: "iPhone", descripción: "iPhone es
el teléfono con más estilo", precio: 1000, imagen: "iphone.png"), ProductBox( nombre:
"Pixel", descripción: " Pixel es el teléfono con más funciones de la historia", precio:
800, imagen: "pixel.png"), ProductBox (nombre: "Laptop", descripción: "La
computadora portátil es la herramienta de desarrollo más productiva",

51
Machine Translated by Google

Aleteo

precio: 2000,
imagen: "laptop.png"),

ProductBox( name:
"Tablet", description: "Tablet es el dispositivo más útil para
reunión",
precio: 1500,
imagen: "tablet.png"),

ProductBox( nombre:
"Pendrive", descripción: "Pendrive es un medio de
almacenamiento útil", precio: 100, imagen: "pendrive.png"),

CajaProducto(
nombre: "Unidad de disquete",
descripción: "La unidad de disquete es un almacenamiento de rescate útil
medio",
precio: 20,
imagen: "floppy.png"),

], ));
}
}

• El código completo de la aplicación es el siguiente:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product layout
demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@anular
Widget build (contexto BuildContext) { return
Scaffold (appBar: AppBar (título: Texto
("Lista de productos")), cuerpo: ListView (

52
Machine Translated by Google

Aleteo

ShrinkWrap: true,
padding: const EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0), children:
<Widget>[ ProductBox( name: "iPhone", description: "iPhone is the stylist
phone ever", price: 1000, image : "iphone.png"),

ProductBox( nombre:
"Pixel", descripción: "Pixel es el teléfono con más funciones", precio:
800, imagen: "pixel.png"), ProductBox( nombre: "Laptop", descripción:
"La computadora portátil es la herramienta de desarrollo más productiva
", precio: 2000, imagen: "laptop.png"),

ProductBox( name:
"Tablet", description: "Tablet es el dispositivo más útil para
reunión",
precio: 1500,
imagen: "tablet.png"),

ProductBox( nombre:
"Pendrive", descripción: "iPhone es el teléfono con más estilo",
precio: 100, imagen: "pendrive.png"), ProductBox(

nombre: "Floppy Drive",


descripción: "iPhone es el teléfono con más estilo", precio: 20,
imagen: "floppy.png"),

ProductBox( nombre:
"iPhone", descripción: "iPhone es el teléfono con más estilo",
precio: 1000, imagen: "iphone.png"),

ProductBox( nombre:
"iPhone", descripción: "iPhone es el teléfono con más estilo",
precio: 1000, imagen: "iphone.png"),

], ));
}
}

clase RatingBox extiende StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

class _RatingBoxState extiende Estado<RatingBox> {

53
Machine Translated by Google

Aleteo

int _calificación = 0;

void _setRatingAsOne()
{ setState( () { _rating = 1; });

void _setRatingAsTwo() {

establecerEstado( ()
{ _puntuación =
2; });
}

void _setRatingAsThree()
{ setState( () { _rating = 3; });

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max, children:
<Widget>[ Container( padding: EdgeInsets.all(0), child:
IconButton(

icon: (_rating >= 1 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsOne, iconSize: _size, ), ),
Contenedor( relleno: EdgeInsets.all(0), child: IconButton(

icon: (_rating >= 2 ? Icon(Icons.star, size: _size,) : Icon(Icons.star_border,


size: _size,)), color: Colors.red[500], onPressed: _setRatingAsTwo, iconSize: _size, ), ),
Contenedor( relleno: EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 3 ? Icono(Iconos.estrella, tamaño: _tamaño) :


Icono(Iconos.estrella_border, tamaño: _tamaño)),

54
Machine Translated by Google

Aleteo

color: Colors.red[500],
onPressed: _setRatingAsThree,
iconSize: _size, ), ),

], );
}
}

class ProductBox extiende StatelessWidget


{ ProductBox({Key key, this.name, this.description, this.price, this.image}) :
super(key: key);

nombre final de la
cadena; descripción final de la
cadena; precio internacional final;
imagen de cadena final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 140, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, niños:


<Widget>[
Image.asset("assets/appimages/" + image),
Expanded( child: Container( padding: EdgeInsets.all(5),
child: Column( mainAxisAlignment:

MainAxisAlignment.spaceEvenly,
children:

<Widget>[ Text(this.name, style: TextStyle(fontWeight:


Peso de la fuente.negrita)),
Text(this.description),
Text("Precio: " + this.price.toString()),
RatingBox(), ], )))

])));
}
}

55
Machine Translated by Google

Aleteo

• Finalmente, ejecute la aplicación y vea la página Gestión de estado - Lista de productos


Resultados como se muestra aquí:

56
Machine Translated by Google

Aleteo

Al hacer clic en la estrella de calificación, se actualizará la calificación del producto. Por ejemplo, establecer 2 estrellas
calificación para iPhone mostrará la calificación de la siguiente manera:

Estado de la aplicación -scoped_model


Flutter proporciona una manera fácil de administrar el estado de la aplicación usando scoped_model
paquete. El paquete Flutter es simplemente una biblioteca de funcionalidad reutilizable. Aprenderemos sobre los paquetes de
Flutter en detalle en los próximos capítulos.

scoped_model proporciona tres clases principales para permitir una gestión de estado robusta en una aplicación que se analizan
en detalle aquí:

Modelo
El modelo encapsula el estado de una aplicación. Podemos usar tantos modelos (heredando la clase Model) como sea necesario
para mantener el estado de la aplicación. Tiene un solo método, notificar a los oyentes, que debe llamarse cada vez que cambia el
estado del modelo. notificarListeners hará las cosas necesarias para actualizar la interfaz de usuario.

clase Producto extiende Modelo {


nombre final de la cadena;
descripción final de la cadena;
precio internacional final;

57
Machine Translated by Google

Aleteo

imagen de cadena final;


calificación interna;

Producto(este.nombre, esta.descripción, este.precio, esta.imagen,


esta.puntuación);

Factory Product.fromMap(Map<String, dynamic> json) { return


Product( json['name'], json['description'], json['price'],
json['image'], json['rating' ], );

void actualizarClasificación(int miClasificación) {


calificación = miCalificación;

notificar a los oyentes ();


}
}

Modelo de alcance
ScopedModel es un widget, que contiene el modelo dado y luego lo pasa a todos los widgets
descendientes si se solicita. Si se necesita más de un modelo, debemos anidar el ScopedModel.

• Modelo único

ScopedModel<Producto>( modelo: artículo, hijo: AnyWidget()


)

• Múltiples modelos

ScopedModel<Producto>( modelo:
item1, child:
ScopedModel<Producto>( modelo:
item2, child: AnyWidget(), ),
)

ScopedModel.of es un método utilizado para obtener el modelo subyacente al ScopedModel. Se puede


usar cuando no se necesitan cambios en la interfaz de usuario aunque el modelo vaya a cambiar. Lo
siguiente no cambiará la IU (calificación) del producto.

ScopedModel.of<Producto>(contexto).updateRating(2);

58
Machine Translated by Google

Aleteo

AlcanceModeloDescendiente
ScopedModelDescendant es un widget, que obtiene el modelo del widget de nivel superior, ScopedModel y
construye su interfaz de usuario cada vez que cambia el modelo.

ScopedModelDescendant tiene dos propiedades: constructor e hijo. child es la parte de la interfaz de usuario
que no cambia y se pasará al constructor. builder acepta una función con tres argumentos:

• contenido - ScopedModelDescendant pasa el contexto de la aplicación.

• child: una parte de la interfaz de usuario que no cambia según el modelo.


• modelo: el modelo real en esa instancia.

devuelve ScopedModelDescendant<ProductModel>(
constructor: (contexto, niño, carrito) => { ... IU real ... }, niño: PartOfTheUI(), );

Cambiemos nuestro ejemplo anterior para usar scoped_model en lugar de StatefulWidget

• Cree una nueva aplicación Flutter en Android Studio, product_scoped_model_app

• Reemplace el código de inicio predeterminado (main.dart) con nuestro código product_state_app.

• Copie la carpeta de activos de product_nav_app a product_rest_app y agregue activos


dentro del archivo pubspec.yaml

aleteo:

activos:
- assets/appimages/floppy.png - assets/
appimages/iphone.png - assets/
appimages/laptop.png - assets/
appimages/pendrive.png - assets/
appimages/pixel.png - assets/appimages/
tablet.png

• Configure el paquete scoped_model en el archivo pubspec.yaml como se muestra a continuación:

dependencias:
scoped_model: ^1.0.1

Aquí, debe usar la última versión del paquete http

• Android Studio alertará que pubspec.yaml está actualizado.

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo
configurará correctamente para la aplicación.

59
Machine Translated by Google

Aleteo

• Reemplace el código de inicio predeterminado (main.dart) con nuestro código de inicio.

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product state
demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
Text( 'Hello World', )),

);
}
}

• Importe el paquete scoped_model en el archivo main.dart

import 'paquete:scoped_model/scoped_model.dart';

• Vamos a crear una clase Product, Product.dart para organizar la información del producto.

import 'paquete:scoped_model/scoped_model.dart';

El producto de clase extiende el modelo


{ nombre final de la cadena; descripción
final de la cadena; precio internacional
final; imagen de cadena final;
calificación interna;

60
Machine Translated by Google

Aleteo

Producto(este.nombre, esta.descripción, este.precio, esta.imagen,


esta.puntuación);

Factory Product.fromMap(Map<String, dynamic> json) { return


Product( json['name'], json['description'], json['price'], json['image'],
json['rating' ], );

void actualizarClasificación(int miClasificación) {


calificación = miCalificación;

notificar a los oyentes ();


}
}

Aquí, hemos usado notificar a los oyentes para cambiar la interfaz de usuario cada vez que se cambia la calificación.

• Escribamos un método getProducts en la clase Product para generar nuestro dummy


registros de productos.

Lista estática<Producto> getProductos() {


List<Producto> items = <Producto>[];

items.add(Product( "Pixel",
"Pixel es el teléfono
con más funciones hasta ahora", 800, "pixel.png", 0));

items.add(Producto( "Laptop", "Laptop es la herramienta de


desarrollo más productiva", 2000, "laptop.png", 0));

items.add(Product( "Tablet",
"Tablet es el
dispositivo más útil para reuniones", 1500, "tablet.png", 0));

items.add(Producto( "Pendrive", "Pendrive es un medio


de almacenamiento útil", 100, "pendrive.png", 0));

items.add(Producto(

61
Machine Translated by Google

Aleteo

"Unidad de disquete",
"La unidad de disquete es un medio de almacenamiento de rescate
útil", 20, "floppy.png", 0));

devolver los artículos;


}

importar producto.dart en main.dart

importar 'Producto.dardo';

• Cambiemos nuestro nuevo widget, RatingBox para admitir el concepto scoped_model.

class RatingBox extiende StatelessWidget { RatingBox({Key


key, this.item}) : super(key: key);

artículo del producto final;

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
print(artículo.puntuación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize:
MainAxisSize.max, children: <Widget>[ Container( padding:
EdgeInsets.all(0), child: IconButton( icon: (item.rating >= 1 ?
Icono( Iconos.estrella, tamaño: _tamaño,

)
:
Icon( Icons.star_border, size:
_size, )), color:
Colors.red[500], onPressed:
() => this.item.updateRating(1),
iconSize: _size, ), ), Container( padding: EdgeInsets.all(0),
child: IconButton( icon: (item.rating >= 2 ? Icon( Icons.star,
size: _size,

)
: Icono
(Iconos.star_border,
tamaño: _tamaño,

62
Machine Translated by Google

Aleteo

)),
color: Colors.red[500],
onPressed: () => this.item.updateRating(2), iconSize:
_size, ), ), Container( padding: EdgeInsets.all(0), child:
IconButton( icono: (artículo.valoración >= 3 ?
Icono( Iconos.estrella, tamaño: _tamaño,

)
:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500], onPressed:
() => this.item.updateRating(3),
iconSize: _size, ), ), ], );

}
}

Aquí, hemos ampliado el RatingBox de StatelessWidget en lugar de StatefulWidget.


Además, hemos utilizado el método updateRating del modelo de producto para establecer la calificación.

• Modifiquemos nuestro widget ProductBox para que funcione con Product, ScopedModel y
Clase ScopedModelDescendant.

class ProductBox extiende StatelessWidget {


ProductBox({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all (2),
altura: 140, niño: Tarjeta ( niño: Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + this.item.image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
ScopedModel<Product >( modelo: este.elemento, hijo: Columna(

63
Machine Translated by Google

Aleteo

mainAxisAlignment:
MainAxisAlignment.spaceEvenly, niños: <Widget>[

Text(this.item.name,
style:
TextStyle(fontWeight:
Peso de la fuente.negrita)),
Texto(este.artículo.descripción),
Texto("Precio: " +
este.artículo.precio.toString()),
ScopedModelDescendant<Producto>(
constructor: (contexto, hijo, artículo) {
devuelve RatingBox (elemento:
elemento); }) ], ))))

]),
));
}
}

Aquí, hemos envuelto el widget RatingBox dentro de ScopedModel y ScopedModelDecendant.

• Cambie el widget MyHomePage para usar nuestro widget ProductBox.

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

artículos finales = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title:
Text("Navegación del producto")), cuerpo: ListView.builder(

itemCount: items.length,
itemBuilder: (contexto, índice) { return
ProductBox(item: items[index]); }, ));

}
}

Aquí, hemos usado ListView.builder para construir dinámicamente nuestra lista de productos.

• El código completo de la aplicación es el siguiente:

Producto.dardo

import 'paquete:scoped_model/scoped_model.dart';

clase Producto extiende Modelo {

64
Machine Translated by Google

Aleteo

nombre final de la cadena;


descripción final de la cadena;
precio internacional final; imagen
de cadena final; calificación interna;

Producto(este.nombre, esta.descripción, este.precio, esta.imagen, esta.puntuación);

Factory Product.fromMap(Map<String, dynamic> json) { return


Product( json['name'], json['description'], json['price'], json['image'],
json['rating' ], );n

vacío cn
"La computadora portátil es la herramienta de desarrollo más
productiva", 2000, "laptop.png", 0));

items.add(Product( "Tablet"cnvn, "Tablet es el dispositivo más útil para reuniones",


1500, "tablet.png", 0));

items.add(Producto( "Pendrive", "Pendrive es un medio


de almacenamiento útil", 100, "pendrive.png", 0));

items.add(Producto( "Unidad
de disquete", "Unidad
de disquete es un medio de almacenamiento de rescate útil", 20,
"disquete.png", 0));

devolver los artículos;


}
}
dardo principal

importar 'paquete: flutter/material.dart'; import


'paquete:scoped_model/scoped_model.dart'; importar 'Producto.dardo';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este widget


es la raíz de su aplicación.

sesenta y cinco
Machine Translated by Google

Aleteo

@anular
Compilación del widget (contexto BuildContext) {
return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product state
demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

artículos finales = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title:
Text("Navegación del producto")), cuerpo: ListView.builder(

itemCount: items.length,
itemBuilder: (contexto, índice) { return
ProductBox(item: items[index]); }, ));

}
}

class RatingBox extiende StatelessWidget {


RatingBox({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
print(artículo.puntuación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max, children:
<Widget>[ Container( padding: EdgeInsets.all(0), child:
IconButton( icon: (item.rating >= 1 ?
Icono( Iconos.estrella, tamaño: _tamaño,

)
: Icono(

66
Machine Translated by Google

Aleteo

Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: () =>
this.item.updateRating(1), iconSize: _size, ), ),
Container( padding: EdgeInsets.all (0), child:
IconButton( icon: (item.rating >= 2 ? Icon( Icons.star, size:
_size,

)
:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: () =>
this.item.updateRating(2), iconSize: _size, ), ),
Container( padding: EdgeInsets.all(0), child:
IconButton( icon: (item.rating >= 3 ? Icon( Icons.star, size:
_size,

)
:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: () =>
this.item.updateRating(3), iconSize: _size, ), ), ], );

}
}

class ProductBox extiende StatelessWidget {


ProductBox({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

Compilación del widget (contexto BuildContext)


{ contenedor de retorno (relleno:
EdgeInsets.all (2),

67
Machine Translated by Google

Aleteo

altura: 140,
hijo: Tarjeta
(hijo: Fila (
mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:
<Widget>[ Image.asset("assets/appimages/" + this.item.image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
ScopedModel<Product >( modelo: this.item, child:
Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[

Text(this.item.name, style:
TextStyle(fontWeight:

Peso de la fuente.negrita)),
Texto(este.artículo.descripción),
Texto("Precio: " +
este.artículo.precio.toString()),
ScopedModelDescendant<Producto>(
constructor: (contexto, hijo, elemento) {
devuelve RatingBox (elemento:
elemento); }) ], ))))

]),
));
}
}

Finalmente, compile y ejecute la aplicación para ver su resultado. Funcionará de manera similar al ejemplo
anterior, excepto que la aplicación usa el concepto scoped_model.

Navegación y enrutamiento
En cualquier aplicación, navegar de una página/pantalla a otra define el flujo de trabajo de la aplicación. La
forma en que se maneja la navegación de una aplicación se llama enrutamiento.
Flutter proporciona una clase de enrutamiento básica: MaterialPageRoute y dos métodos: Navigator.push y
Navigator.pop, para definir el flujo de trabajo de una aplicación.

MaterialPageRoute
MaterialPageRoute es un widget que se usa para representar su interfaz de usuario al reemplazar toda la pantalla con una
animación específica de la plataforma.

MaterialPageRoute(constructor: (contexto) => Widget())

Aquí, el constructor aceptará una función para construir su contenido proporcionando el contexto actual de la
aplicación.

Navegación.push

68
Machine Translated by Google

Aleteo

Navigation.push se usa para navegar a una nueva pantalla usando el widget MaterialPageRoute.

Navigator.push(contexto,

MaterialPageRoute(constructor: (contexto) => Widget()),


);

Navegación.pop
Navigation.pop se utiliza para navegar a la pantalla anterior.

Navigator.push(contexto);

Vamos a crear una nueva aplicación para comprender mejor el concepto de navegación.

Cree una nueva aplicación Flutter en Android Studio, product_nav_app

• Copie la carpeta de activos de product_nav_app a product_state_app y agregue activos


dentro del archivo pubspec.yaml

aleteo:

activos:
- assets/appimages/floppy.png - assets/
appimages/iphone.png - assets/
appimages/laptop.png - assets/
appimages/pendrive.png - assets/
appimages/pixel.png - assets/appimages/
tablet.png

• Reemplace el código de inicio predeterminado (main.dart) con nuestro código de inicio.

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget compilación
(contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', tema:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product state
demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

69
Machine Translated by Google

Aleteo

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
Text( 'Hello World', )),

);
}
}

• Vamos a crear una clase Producto para organizar la información del producto.

clase Producto
{ cadena final nombre;
descripción final de la cadena;
precio internacional final; imagen
de cadena final;

Producto(este.nombre, esta.descripción, este.precio, esta.imagen);


}

• Escribamos un método getProducts en la clase Product para generar nuestro dummy


registros de productos.

Lista estática<Producto> getProductos() {


List<Producto> items = <Producto>[];

items.add(Product( "Pixel",
"Pixel es el
teléfono con más funciones hasta ahora", 800,
"pixel.png"));

items.add(Producto( "Laptop", "Laptop es la herramienta de


desarrollo más productiva", 2000, "laptop.png"));

items.add(Producto( "Tableta", "La tableta es el dispositivo más útil para


reuniones", 1500, "tableta.png"));

items.add(Producto( "Pendrive", "Pendrive es un medio de almacenamiento útil",

70
Machine Translated by Google

Aleteo

100,
"pendrive.png"));

items.add(Producto( "Unidad
de disquete", "La
unidad de disquete es un medio de almacenamiento de rescate
útil", 20, "disquete.png"));

devolver los artículos;


}
importar producto.dart en main.dart

importar 'Producto.dardo';

• Incluyamos nuestro nuevo widget, RatingBox

clase RatingBox extiende StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

class _RatingBoxState extiende Estado<RatingBox> {


int _calificación = 0;

void _setRatingAsOne()
{ setState(() { _rating = 1; });

void _setRatingAsTwo()
{ setState(() { _rating = 2; });

void _setRatingAsThree() {
setState(()
{ _puntuación
= 3; });
}

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.max, children:
<Widget>[ Container( padding: EdgeInsets.all(0), child:
IconButton(

71
Machine Translated by Google

Aleteo

icono: (_calificación >= 1 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: _setRatingAsOne,
iconSize: _size, ), ),
Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 2 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: _setRatingAsTwo,
iconSize: _size, ), ),
Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 3 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: _setRatingAsThree,
iconSize: _size, ), ), ], );

}
}

72
Machine Translated by Google

Aleteo

• Modifiquemos nuestro widget ProductBox para que funcione con nuestra nueva clase Product.

class ProductBox extiende StatelessWidget


{ ProductBox({Key key, this.item}) : super(key: key);

artículo del producto final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 140, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + this.item.image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
Column( mainAxisAlignment : MainAxisAlignment.spaceEvenly,

niños: <Widget>[
Text(this.item.name,
style: TextStyle(fontWeight:
Peso de la fuente.negrita)),
Text(this.item.description),
Text("Precio: " + this.item.price.toString()), RatingBox(), ], )))

]),
));
}
}

• Reescribamos nuestro widget MyHomePage para que funcione con el modelo de Producto y para listar todos
los productos usando ListView.

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

artículos finales = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title:
Text("Navegación del producto")), cuerpo: ListView.builder(

itemCount: items.length,
itemBuilder: (contexto, índice) {

73
Machine Translated by Google

Aleteo

return GestureDetector( child:


ProductBox(item: items[index]), onTap: ()
{ Navigator.push( context, MaterialPageRoute(

constructor: (contexto) => ProductPage(elemento:


artículos[índice]),

), ); }, ); }, ));

}
}

Aquí, hemos utilizado MaterialPageRoute para navegar a la página de detalles del producto.

• Ahora, agreguemos ProductPage para mostrar los detalles del producto.

clase ProductPage extiende StatelessWidget {


ProductPage({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.item.name), ), body: Center( child:
Container( padding: EdgeInsets.all(0),

child:
Column( mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children:
<Widget>[ Image.asset("assets/appimages/" +

esta.imagen.del.elemento),

Expandido( hijo:
Contenedor( relleno: EdgeInsets.all(5),
hijo: Columna( mainAxisAlignment:

MainAxisAlignment.spaceEvenly,
niños: <Widget>[
Text(this.item.name, style:
TextStyle(fontWeight:
Peso de la fuente.negrita)),
Texto(este.artículo.descripción),
Texto("Precio: " +
este.artículo.precio.toString()),
Cuadro de clasificación(),

74
Machine Translated by Google

Aleteo

], )))
]),
),

), );
}
}

• El código completo de la aplicación es el siguiente:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

clase Producto
{ cadena final nombre;
descripción final de la cadena;
precio internacional final; imagen
de cadena final;

Producto(este.nombre, esta.descripción, este.precio, esta.imagen);

Lista estática<Producto> getProductos() {


List<Producto> artículos = <Producto>[];

items.add(Producto( "Pixel",
"Pixel es el teléfono con más funciones de la historia", 800,
"píxel.png"));

items.add(Product("Laptop", "Laptop es la herramienta de desarrollo más productiva",

2000, "portátil.png"));

items.add(Producto( "Tableta", "La tableta es el dispositivo más útil para


reuniones", 1500, "tableta.png"));

items.add(Producto( "Pendrive", "iPhone es el teléfono con más estilo",


100, "pendrive.png"));

items.add(Producto( "Disquete", "iPhone es el teléfono más estilista de todos


los tiempos", 20, "floppy.png"));

items.add(Producto( "iPhone", "iPhone es el teléfono con más estilo",


1000, "iphone.png"));

devolver los artículos;


}
}

75
Machine Translated by Google

Aleteo

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', theme:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(title: 'Product
Navigation demo home page'), );

}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

artículos finales = Product.getProducts();

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title:
Text("Navegación del producto")), cuerpo: ListView.builder(

itemCount: items.length,
itemBuilder: (contexto, índice) { return
GestureDetector( child:
ProductBox(item: items[index]), onTap: ()
{ Navigator.push( context, MaterialPageRoute(

constructor: (contexto) => ProductPage(item:


artículos[índice]),

), ); }, ); }, ));

}
}

clase ProductPage extiende StatelessWidget {


ProductPage({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

@anular
Widget compilación (contexto BuildContext)
{ return Scaffold (appBar: AppBar (

76
Machine Translated by Google

Aleteo

title: Text(this.item.name), ), body:


Center( child: Container( padding:
EdgeInsets.all(0), child:
Column( mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start, children: <Widget
>[ Image.asset("assets/appimages/" + this.item.image),
Expanded( child: Container( padding: EdgeInsets.all(5),
child: Column( mainAxisAlignment:

MainAxisAlignment.spaceEvenly,
niños: <Widget>[
Text(this.item.name, style:
TextStyle(fontWeight:
Peso de la fuente.negrita)),
Texto(este.artículo.descripción),
Texto("Precio: " +
este.artículo.precio.toString()),
Cuadro de
clasificación(), ], )))

]),
),

), );
}
}

clase RatingBox extiende StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

class _RatingBoxState extiende Estado<RatingBox> {


int _calificación = 0;

void _setRatingAsOne()
{ setState(() { _rating = 1; });

void _setRatingAsTwo()
{ setState(() { _rating = 2; });

void _setRatingAsThree()
{ setState(() {

77
Machine Translated by Google

Aleteo

_puntuación =
3; });
}

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize:
MainAxisSize.max, children: <Widget>[ Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 1 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border, size:
_size, )), color:
Colors.red[500], onPressed:
_setRatingAsOne, iconSize:
_size, ), ), Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 2 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border, size:
_size, )), color:
Colors.red[500], onPressed:
_setRatingAsTwo, iconSize:
_size, ), ), Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 3 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)

78
Machine Translated by Google

Aleteo

:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: _setRatingAsThree,
iconSize: _size, ), ), ], );

}
}

class ProductBox extiende StatelessWidget {


ProductBox({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 140, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + this.item.image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
Column( mainAxisAlignment : MainAxisAlignment.spaceEvenly,
hijos: <Widget>[

Text(this.item.name,
style: TextStyle(fontWeight:
Peso de la fuente.negrita)),
Text(this.item.description),
Text("Precio: " + this.item.price.toString()), RatingBox(), ], )))

]),
));
}
}

79
Machine Translated by Google

Aleteo

Ejecute la aplicación y haga clic en cualquiera de los elementos del producto. Mostrará la página de
detalles relevante. Podemos pasar a la página de inicio haciendo clic en el botón Atrás. La página de lista
de productos y la página de detalles de productos de la aplicación se muestran a continuación:

80
Machine Translated by Google

Aleteo

81
Machine Translated by Google

Aleteo
10. Aleteo – Animación

La animación es un procedimiento complejo en cualquier aplicación móvil. A pesar de su complejidad, la


animación mejora la experiencia del usuario a un nuevo nivel y proporciona una rica interacción con el
usuario. Debido a su riqueza, la animación se convierte en una parte integral de las aplicaciones móviles
modernas. Flutter framework reconoce la importancia de la animación y proporciona una
marco simple e intuitivo para desarrollar todo tipo de animaciones.

Introducción
La animación es un proceso de mostrar una serie de imágenes/imagen en un orden particular dentro de
una duración específica para dar una ilusión de movimiento. Los aspectos más importantes de la animación
son los siguientes:

• La animación tiene dos valores distintos: valor inicial y valor final. La animación comienza desde el valor
inicial y pasa por una serie de valores intermedios y finalmente termina en los valores finales . Por
ejemplo, para animar un widget para que desaparezca, el valor inicial será la opacidad total y el valor
final será la opacidad cero.

• Los valores intermedios pueden ser de naturaleza lineal o no lineal (curva) y se pueden configurar.
Comprenda que la animación funciona tal como está configurada. Cada configuración proporciona
una sensación diferente a la animación. Por ejemplo, el desvanecimiento de un widget será de
naturaleza lineal, mientras que el rebote de una pelota será de naturaleza no lineal.

• La duración del proceso de animación afecta la velocidad (lentitud o rapidez) del


animación.

• La capacidad de controlar el proceso de animación como iniciar la animación, detener la animación,


repetir la animación para establecer un número determinado de veces, invertir el proceso de
animación, etc.

• En Flutter, el sistema de animación no hace ninguna animación real. En su lugar, proporciona solo
los valores necesarios en cada cuadro para representar las imágenes.

Clases basadas en animación


El sistema de animación Flutter se basa en objetos de animación. Las clases principales de animación y
su uso son las siguientes:

Animación
Genera valores interpolados entre dos números durante un tiempo determinado. Las clases de Animación
más comunes son:

• Animación<doble> - interpolar valores entre dos números decimales

• Animation<Color> - interpolar colores entre dos colores

• Animación<Tamaño> - interpolar tamaños entre dos tamaños

82
Machine Translated by Google

Aleteo

• AnimationController: objeto de animación especial para controlar la propia animación. Genera nuevos valores cada
vez que la aplicación está lista para un nuevo marco. Admite animación lineal y el valor comienza de 0.0 a 1.0.

controlador = AnimationController (duración: const Duración (segundos: 2), vsync: esto);

Aquí, el controlador controla la animación y la opción de duración controla la duración del proceso de animación.
vsync es una opción especial que se utiliza para optimizar el recurso utilizado en la animación.

CurvaAnimación
Similar a AnimationController pero admite animación no lineal. CurvedAnimation se puede usar junto con el objeto de
animación de la siguiente manera:

controlador = AnimationController (duración: const Duración (segundos: 2), vsync: esto);

animación = CurvedAnimation(padre: controlador, curva: Curves.easeIn)

Interpolación<T>

Derivado de Animatable<T> y utilizado para generar números entre dos números que no sean 0 y 1. Se puede utilizar junto
con el objeto de animación utilizando el método de animación y pasando el objeto de animación real.

Controlador de controlador de animación = Controlador de animación (


duración: const Duración (milisegundos: 1000), vsync: esto);
Animation<int> customTween = IntTween(begin: 0, end: 255).animate(controller);

Tween también se puede usar junto con CurvedAnimation como se muestra a continuación:

Controlador AnimationController = AnimationController(duración: const Duración(milisegundos: 500),


vsync: esto);
curva de animación final = CurvedAnimation (padre: controlador, curva: Curves.easeOut);

Animation<int> customTween = IntTween(begin: 0, end: 255).animate(curve);

Aquí, el controlador es el controlador de animación real. curve proporciona el tipo de no linealidad y customTween proporciona
un rango personalizado de 0 a 255.

Flujo de trabajo de FlutterAnimation


El flujo de trabajo de la animación es el siguiente:

• Defina e inicie el controlador de animación en el initState del StatefulWidget.

AnimationController(duración: const Duración(segundos: 2), vsync: esto); animación = Tween<doble>(inicio:


0, fin: 300).animate(controlador);
controlador.adelante();

• Agregar oyente basado en animación, addListener para cambiar el estado del widget

83
Machine Translated by Google

Aleteo

animation = Tween<doble>(begin: 0, end: 300).animate(controller) ..addListener(()


{ setState(() { // El estado que ha cambiado aquí es el del objeto de animación

valor. });

});

• Se pueden usar widgets incorporados, AnimatedWidget y AnimatedBuilder para omitir este proceso. Ambos
widgets aceptan el objeto Animación y obtienen los valores actuales necesarios para la animación.

• Obtenga los valores de la animación durante el proceso de creación del widget y luego aplíquelo para el
ancho, la altura o cualquier propiedad relevante en lugar del valor original.

niño: Contenedor( altura:


animación.valor, ancho:
animación.valor, niño: <Widget>,

Aplicación de trabajo
Escribamos una aplicación simple basada en animación para comprender el concepto de animación en el marco
Flutter.

• Cree una nueva aplicación Flutter en Android Studio, product_animation_app

• Copie la carpeta de activos de product_nav_app a product_animation_app y agregue activos dentro del


archivo pubspec.yaml

aleteo:

activos:
- activos/imágenes de la aplicación/disquete.png
- activos/imágenes de la aplicación/iphone.png
- activos/imágenes de la aplicación/portátil.png
- activos/imágenes de la aplicación/pendrive.png
- activos/imágenes de la aplicación/pixel.png
- activos/imágenes de la aplicación/tableta.png

• Eliminar el código de inicio predeterminado (main.dart).

• Agregar importación y función principal básica

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

• Crear el widget MyApp derivado de StatefulWidgtet

clase MyApp extiende StatefulWidget {


_MyAppState createState() => _MyAppState();
}

84
Machine Translated by Google

Aleteo

• Cree el widget _MyAppState e implemente initState y elimínelo además del método de compilación
predeterminado.

class _MyAppState extiende State<MyApp> con SingleTickerProviderStateMixin {

Animación<doble> animación;
Controlador AnimationController;

@override
void initState() {
super.initState();
controlador = AnimationController (duración: const Duración (segundos: 10), vsync: esto);

animación = Tween<doble>(inicio: 0.0, fin: 1.0).animate(controlador); controlador.adelante();

// Este widget es la raíz de su aplicación. @override Widget


build(BuildContext context) { controller.forward(); return
MaterialApp(título: 'Flutter Demo', tema:
ThemeData(primarySwatch: Colors.blue, ), home:
MyHomePage(título: 'Product layout demo home page',
animation: animation,)

);
}

@override
void dispose()
{ controlador.dispose();
super.dispose();
}
}

Aquí,

• En el método initState, hemos creado un objeto de controlador de animación (controlador), un objeto


de animación (animación) y comenzamos la animación usando controller.forward.

• En el método dispose, hemos eliminado el objeto del controlador de animación (controller).

• En el método de compilación, envíe la animación al widget MyHomePage a través del constructor.


Ahora, el widget MyHomePage puede usar el objeto de animación para animar su contenido.

• Ahora, agregue el widget ProductBox

class ProductBox extiende StatelessWidget


{ ProductBox({Clave clave, este.nombre, esta.descripción, este.precio,
esta.imagen})

85
Machine Translated by Google

Aleteo

: super(clave: clave);

nombre final de la
cadena; descripción final de la
cadena; precio internacional final;
imagen de cadena final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 140, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly ,
children: <Widget>[ Text(this.name, style: TextStyle(fontWeight:

Peso de la fuente.negrita)),
Text(this.description),
Text("Precio: " + this.price.toString()), ], )))

])));
}
}

• Cree un nuevo widget, MyAnimatedWidget para hacer una animación de fundido simple usando opacidad.

class MyAnimatedWidget extiende StatelessWidget


{ MyAnimatedWidget({this.child, this.animation});

Widget final hijo;


Animación final<doble> animación;

Widget compilación (contexto BuildContext) => Centro


(hijo: AnimatedBuilder (
animación: animación,
constructor: (contexto, hijo) => Contenedor( hijo:
Opacidad(opacidad: animación.valor, hijo: hijo), hijo: hijo),

);
}

• Aquí, hemos usado AniatedBuilder para hacer nuestra animación. AnimatedBuilder es un widget que
construye su contenido mientras hace la animación al mismo tiempo. Acepta un objeto de animación
para obtener el valor de animación actual. Hemos utilizado animación.

86
Machine Translated by Google

Aleteo

value, animation.value para establecer la opacidad del widget secundario. En efecto, el widget
animará al widget secundario utilizando el concepto de opacidad.
• Finalmente, cree el widget MyHomePage y use el objeto de animación para animar cualquier
parte de su contenido.

clase MyHomePage extiende StatelessWidget {


MyHomePage({Clave clave, este.título, esta.animación}) : super(clave: clave);

título final de la cadena;


Animación final<doble> animación;

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title: Text("Product
Listing")), cuerpo: ListView(shrinkWrap: true, padding: const
EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0) , niños:
<Widget>[ FadeTransition(child: ProductBox( nombre:
"iPhone", descripción: "iPhone es el teléfono con más estilo", precio: 1000,
imagen: "iphone.png"), opacidad: animación), MyAnimatedWidget( niño:
ProductBox (nombre: "Pixel", descripción: "Pixel es el teléfono con más
funciones", precio: 800, imagen: "pixel.png"),

animación: animación),
ProductBox( nombre:
"Laptop", descripción:
"Laptop es la herramienta de desarrollo más productiva", precio: 2000,
imagen: "laptop.png"), ProductBox( nombre: "Tablet", descripción: "Tablet
is el dispositivo más útil para

reunión",
precio: 1500,
imagen: "tablet.png"),
ProductBox (nombre: "Pendrive",
descripción: "Pendrive es
un medio de almacenamiento útil", precio: 100, imagen:
"pendrive.png"), ProductBox (nombre: "Floppy Drive",
descripción: "La unidad de disquete es un almacenamiento de
rescate útil

medio",
precio: 20,
imagen: "floppy.png"),
],

87
Machine Translated by Google

Aleteo

));
}
}

Aquí, hemos usado FadeAnimation y MyAnimationWidget para animar los dos primeros elementos de
la lista. FadeAnimation es una clase de animación incorporada, que usamos para animar a su hijo
usando el concepto de opacidad.

• El código completo es el siguiente:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

clase MyApp extiende StatefulWidget {


_MyAppState createState() => _MyAppState();
}

class _MyAppState extiende State<MyApp> con SingleTickerProviderStateMixin {

Animación<doble> animación;
Controlador AnimationController;

@override
void initState() {
super.initState();
controlador = AnimationController (duración: const Duración (segundos: 10), vsync:
esto); animación = Tween<doble>(inicio: 0.0, fin: 1.0).animate(controlador);
controlador.adelante();

// Este widget es la raíz de su aplicación. @override Widget


build(BuildContext context) { controller.forward(); return
MaterialApp(título: 'Flutter Demo', tema:
ThemeData(primarySwatch: Colors.blue, ), home:
MyHomePage(título: 'Product layout demo home page',
animation: animation,)

);
}

@override
void dispose()
{ controlador.dispose();
super.dispose();
}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Clave clave, este.título, esta.animación}) : super(clave: clave);

88
Machine Translated by Google

Aleteo

título final de la cadena;


Animación final<doble> animación;

@override
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title: Text("Product
Listing")), cuerpo: ListView(shrinkWrap: true, padding: const
EdgeInsets.fromLTRB(2.0, 10.0, 2.0, 10.0) , niños:
<Widget>[ FadeTransition(child: ProductBox( nombre:
"iPhone", descripción: "iPhone es el teléfono con más estilo", precio:
1000, imagen: "iphone.png"), opacidad: animación), MyAnimatedWidget( niño:
ProductBox (nombre: "Pixel", descripción: "Pixel es el teléfono con más
funciones", precio: 800, imagen: "pixel.png"),

animación: animación),
ProductBox( nombre:
"Laptop", descripción:
"Laptop es la herramienta de desarrollo más productiva", precio: 2000,
imagen: "laptop.png"), ProductBox( nombre: "Tablet", descripción: "Tablet
is el dispositivo más útil para

reunión",
precio: 1500,
imagen: "tablet.png"),

ProductBox( nombre:
"Pendrive", descripción: "Pendrive es un medio de
almacenamiento útil", precio: 100, imagen: "pendrive.png"),
ProductBox(

nombre: "Unidad de disquete",


descripción: "La unidad de disquete es un almacenamiento de rescate útil
medio",
precio: 20,
imagen: "floppy.png"),

], ));
}
}

class ProductBox extiende StatelessWidget {


ProductBox({Clave clave, este.nombre, esta.descripción, este.precio,
esta.imagen}) : super(clave: clave);

89
Machine Translated by Google

Aleteo

nombre final de la
cadena; descripción final de la
cadena; precio internacional final;
imagen de cadena final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 140, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly ,
children: <Widget>[ Text(this.name, style: TextStyle(fontWeight:

Peso de la fuente.negrita)),
Text(this.description),
Text("Precio: " + this.price.toString()), ], )))

])));
}
}

class MyAnimatedWidget extiende StatelessWidget


{ MyAnimatedWidget({this.child, this.animation});

Widget final hijo;


Animación final<doble> animación;

Widget compilación (contexto BuildContext) => Centro


(hijo: AnimatedBuilder (
animación: animación,
constructor: (contexto, hijo) => Contenedor( hijo:
Opacidad(opacidad: animación.valor, hijo: hijo), hijo: hijo),

);
}

90
Machine Translated by Google

Aleteo

• Compile y ejecute la aplicación para ver los resultados. La versión inicial y final de
la aplicación es la siguiente:

91
Machine Translated by Google

Aleteo

92
Machine Translated by Google

Aleteo
11. Flutter: escritura de código específico de Android

Flutter proporciona un marco general para acceder a funciones específicas de la plataforma. Esto permite que el
desarrollador amplíe la funcionalidad del marco Flutter utilizando código específico de la plataforma.
Se puede acceder fácilmente a la funcionalidad específica de la plataforma, como la cámara, el nivel de la batería, el
navegador, etc., a través del marco.

La idea general de acceder al código específico de la plataforma es a través de un protocolo de mensajería simple. El
código de Flutter, el Cliente y el código de la plataforma y el Host se unen a un Canal de Mensajes común. El cliente
envía un mensaje al anfitrión a través del canal de mensajes. El host escucha en el canal de mensajes, recibe el mensaje
y realiza la funcionalidad necesaria y, finalmente, devuelve el resultado al cliente a través del canal de mensajes.

La arquitectura de código específica de la plataforma se muestra en el diagrama de bloques que se muestra a continuación:

El protocolo de mensajería utiliza un códec de mensajes estándar (clase StandardMessageCodec) que admite la
serialización binaria de valores similares a JSON, como números, cadenas, booleanos, etc. La serialización y la
deserialización funcionan de forma transparente entre el cliente y el
anfitrión.

Escribamos una aplicación simple para abrir un navegador usando el SDK de Android y comprendamos cómo invocar el
SDK desde la aplicación flutter.

• Cree una nueva aplicación Flutter en Android Studio, flutter_browser_app

• Reemplace el código main.dart con el siguiente código:

importar 'paquete: flutter/material.dart';

void main() => runApp(MiAplicación());

class MyApp extiende StatelessWidget {


@anular
Compilación del widget (contexto BuildContext) {
devolver MaterialApp(
título: 'Demostración de Flutter',
tema: TemaDatos(
muestra primaria: Colors.blue,
),
home: MyHomePage(título: 'Página de inicio de demostración de Flutter'),

93
Machine Translated by Google

Aleteo

);
}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
RaisedButton( child: Text('Open
Browser'), onPressed: null, ) , ), );

}
}

• Aquí, hemos creado un nuevo botón para abrir el navegador y establecer su método onPressed
como nulo.

• Ahora, importe los siguientes paquetes:

importar 'dardo: asíncrono';


import 'paquete: flutter/services.dart';

• Aquí, services.dart incluye la funcionalidad para invocar el código específico de la plataforma.

• Crear un nuevo canal de mensajes en el widget MyHomePage.

static const plataforma = const


MethodChannel('flutterapp.tutorialspoint.com/browser');

• Escriba un método, _openBrowser para invocar el método específico de la plataforma, el método


openBrowser a través del canal de mensajes.

Future<void> _openBrowser() asíncrono { try


{ final int result = await
platform.invokeMethod('openBrowser',
<String, String>{ 'url':
"https://flutter.dev" });

} en la captura PlatformException (e) {


// Imposible abrir el navegador print(e);

}
}

94
Machine Translated by Google

Aleteo

Aquí, hemos usado platform.invokeMethod para invocar openBrowser (explicado en los próximos pasos).
openBrowser tiene un argumento, url para abrir una url específica.

• Cambiar el valor de la propiedad onPressed del RaisedButton de nulo a _openBrowser.

onPressed: _openBrowser,

• Abra MainActivity.java (dentro de la carpeta android) e importe la biblioteca requerida:

importar android.app.Actividad;
importar android.content.Intent; importar
android.net.Uri; importar
android.os.Bundle;

importar io.flutter.app.FlutterActivity; importar


io.flutter.plugin.common.MethodCall; importar
io.flutter.plugin.common.MethodChannel; importar
io.flutter.plugin.common.MethodChannel.MethodCallHandler; importar
io.flutter.plugin.common.MethodChannel.Result; importar
io.flutter.plugins.GeneratedPluginRegistrant;

• Escriba un método, openBrowser para abrir un navegador

private void openBrowser (MethodCall call, Result result, String url) { Actividad actividad =
esto; if (actividad == nulo) { result.error("ACTIVITY_NOT_AVAILABLE", "No se puede
abrir el navegador

sin actividad en primer plano", null); return;

Intención intención = nueva Intención (Intent.ACTION_VIEW);


intent.setData(Uri.parse(url));

actividad.startActivity(intención);
resultado.éxito((Objeto) verdadero);
}

• Ahora, establezca el nombre del canal en la clase MainActivity:

Private static final String CHANNEL =


"flutterapp.tutorialspoint.com/browser";

• Escriba un código específico de Android para configurar el manejo de mensajes en el método onCreate.

new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( new


MethodCallHandler() { @Override public void onMethodCall(llamada de MethodCall,
resultado del resultado) {

Cadena url = llamada.argumento("url");

if (llamada.método.equals("openBrowser")) {

95
Machine Translated by Google

Aleteo

openBrowser(llamada, resultado, url); }


else { resultado.noImplementado();

}
});

Aquí, hemos creado un canal de mensajes usando la clase MethodChannel y usamos la clase
MethodCallHandler para manejar el mensaje. onMethodCall es el método real responsable de llamar al
código específico de la plataforma correcta al verificar el mensaje. El método onMethodCall extrae la URL
del mensaje y luego invoca a openBrowser solo cuando la llamada al método es openBrowser. De lo
contrario, devuelve el método no implementado.

El código fuente completo de la aplicación es el siguiente:

dardo principal

MainActivity.java

paquete com.tutorialspoint.flutterapp.flutter_browser_app;

importar android.app.Actividad;
importar android.content.Intent; importar
android.net.Uri; importar
android.os.Bundle;

importar io.flutter.app.FlutterActivity; importar


io.flutter.plugin.common.MethodCall; importar
io.flutter.plugin.common.MethodChannel.Result; importar
io.flutter.plugins.GeneratedPluginRegistrant;

clase pública MainActivity extiende FlutterActivity { private static


final String CHANNEL = "flutterapp.tutorialspoint.com/browser";

@Override
protected void onCreate(Paquete de estado de instancia guardado) {
super.onCreate(estadoDeInstanciaGuardado);
GeneratedPluginRegistrant.registerWith(this);

new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler( new


MethodCallHandler() { @Override public void onMethodCall(llamada de
MethodCall, resultado del resultado) {

Cadena url = llamada.argumento("url");

if (call.method.equals("openBrowser"))
{ openBrowser(call, result, url); } else
{ resultado.noImplementado();

96
Machine Translated by Google

Aleteo

});
}

private void openBrowser (MethodCall call, Result result, String url) {


Actividad actividad = esto; if
(actividad == nulo)
{ result.error("ACTIVITY_NOT_AVAILABLE", "No se puede abrir el navegador
sin actividad en primer plano", null); return;

Intención intención = nueva Intención (Intent.ACTION_VIEW);


intent.setData(Uri.parse(url));

actividad.startActivity(intención);
resultado.éxito((Objeto) verdadero);
}
}

dardo principal

importar 'paquete: flutter/material.dart';

importar 'dardo: asíncrono';


import 'paquete: flutter/services.dart';

void main() => runApp(MiAplicación());

clase MyApp extiende StatelessWidget


{ @override
Compilación del widget (contexto BuildContext) {
return MaterialApp(título:
'Flutter Demo', theme:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(title: 'Flutter Demo
Home Page'), );

}
}

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

plataforma const estática = const


MethodChannel('flutterapp.tutorialspoint.com/browser'); Future<void>
_openBrowser() asíncrono {
intente
{resultado int final = espera plataforma.invokeMethod('openBrowser',
<String, String>{ 'url':
"https://flutter.dev" });

97
Machine Translated by Google

Aleteo

} en la captura PlatformException (e) {


// Imposible abrir el navegador print(e);

}
}

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
RaisedButton( child: Text('Open
Browser'), onPressed: _openBrowser, ) , ), );

}
}

Ejecute la aplicación y haga clic en el botón Abrir navegador y podrá ver que se inicia el navegador. La
aplicación Navegador - Página de inicio es como se muestra en la captura de pantalla aquí:

98
Machine Translated by Google

Aleteo

99
Machine Translated by Google

Aleteo
12. Flutter: escribir código específico de iOS

El acceso al código específico de iOS es similar al de la plataforma Android, excepto que utiliza lenguajes
específicos de iOS: Objective-C o Swift y SDK de iOS. De lo contrario, el concepto es el mismo que el de la
plataforma Android.

Escribamos también la misma aplicación que en el capítulo anterior para la plataforma iOS.
• Vamos a crear una nueva aplicación en Android Studio (macOS), flutter_browser_ios_app

• Siga los pasos 2 - 6 como en el capítulo anterior.

• Inicie XCode y haga clic en Archivo -> Abrir

• Elija el proyecto xcode en el directorio ios de nuestro proyecto flutter.

• Abra AppDelegate.m en Runner -> Ruta del corredor . Contiene lo siguiente


código:

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementación AppDelegate

- (BOOL)aplicación:(UIApplication *)aplicación
FinalizóLanzamientoConOpciones:(NSDictionary *)launchOptions {

//

[GeneratedPluginRegistrant registerWithRegistry:self];
// Punto de anulación para la personalización después del lanzamiento de la aplicación.
return [súper aplicación: la aplicación finalizó el
lanzamiento con opciones: opciones de lanzamiento];
}

@final

• Hemos agregado un método, openBrowser para abrir el navegador con la URL especificada. Eso
acepta argumento único, url.

- (vacío)navegador abierto:(NSString *)urlString {


NSURL *url = [NSURL URLWithString:urlString];
UIApplication *aplicación = [UIApplication sharedApplication];

[aplicación openURL:url];
}

• En el método didFinishLaunchingWithOptions, encuentre el controlador y configúrelo en el controlador


variable.

100
Machine Translated by Google

Aleteo

FlutterViewController* controlador =
(FlutterViewController*)self.window.rootViewController;

• En el método didFinishLaunchingWithOptions, configura el canal del navegador como


flutterapp.tutorialspoint.com/browse:

FlutterMethodChannel* browserChannel = [FlutterMethodChannel

métodoChannelWithName:@"flutterapp.tutorialspoint.com/browser"
mensajerobinario:controlador];

• Crear una variable, débilSelf y establecer la clase actual:

__tipo débil de (auto) débilAuto = auto;

• Ahora, implemente setMethodCallHandler. Llame a openBrowser haciendo coincidir call.method.


Obtenga url invocando call.arguments y páselo mientras llama a openBrowser.

[browserChannel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult


result) { if ([@"openBrowser" isEqualToString:call.method]) { NSString *url =
call.arguments[@"url"];

[weakSelf openBrowser:url]; } más


{ resultado (FlutterMethodNotImplemented);

}
}];

• El código completo es el siguiente:

#include "AppDelegate.h"
#include "GeneratedPluginRegistrant.h"

@implementación AppDelegate

- (BOOL)aplicación:(UIApplication *)aplicación
FinalizóLanzamientoConOpciones:(NSDictionary *)launchOptions {

// comienza el código personalizado


FlutterViewController* controlador =
(FlutterViewController*)self.window.rootViewController;

FlutterMethodChannel* browserChannel = [FlutterMethodChannel

métodoChannelWithName:@"flutterapp.tutorialspoint.com/browser"
mensajerobinario:controlador];

__tipo débil de (auto) débilAuto = auto;


[browserChannel setMethodCallHandler:^(FlutterMethodCall* llamada,
resultado FlutterResult) {
if ([@"openBrowser" isEqualToString:call.method]) { NSString *url =
call.arguments[@"url"];

101
Machine Translated by Google

Aleteo

[weakSelf openBrowser:url]; } más


{ resultado (FlutterMethodNotImplemented);

}
}];

// finaliza el código personalizado

[GeneratedPluginRegistrant registerWithRegistry:self]; // Punto de


anulación para la personalización después del lanzamiento de la aplicación.
return [súper aplicación: la aplicación finalizó el lanzamiento con opciones:
opciones de lanzamiento]; }

- (vacío)navegador abierto:(NSString *)urlString {


NSURL *url = [NSURL URLWithString:urlString];
UIApplication *aplicación = [UIApplication sharedApplication];

[aplicación openURL:url];
}

@final

• Configuración del proyecto abierto.

• Vaya a Capacidades y habilite Modos en segundo plano

• Agregar *búsqueda de fondo y notificación remota**

• Ahora, ejecute la aplicación. Funciona de manera similar a la versión de Android, pero se abrirá el
navegador Safari en lugar de Chrome.

102
Machine Translated by Google

13. Flutter – Introducción al paquete Aleteo

La forma de Dart de organizar y compartir un conjunto de funcionalidades es a través de Package. Dart Package es
simplemente bibliotecas o módulos compartibles. En general, el paquete Dart es el mismo que el de la aplicación Dart,
excepto que el paquete Dart no tiene un punto de entrada principal a la aplicación.

La estructura general del paquete (considere un paquete de demostración, my_demo_package) es la siguiente:

• lib/src/* : archivos de código Dart privados.

• lib/my_demo_package.dart: Archivo de código principal de Dart . Se puede importar a un


aplicación como:

importar 'paquete: mi_paquete_demo/mi_paquete_demo.dart'

• Se puede exportar otro archivo de código privado al archivo de código principal


(my_demo_package.dart), si es necesario, como se muestra a continuación:

exportar src/mi_código_privado.dart

• lib/* : cualquier número de archivos de código Dart organizados en cualquier estructura de carpetas personalizada.
Se puede acceder al código como,

importar 'paquete: mi_paquete_demo/carpeta_personalizada/archivo_personalizado.dart'

• pubspec.yaml: Especificación del proyecto, igual que la de la aplicación.

Todos los archivos de código de Dart en el Paquete son simplemente clases de Dart y no tienen ningún requisito especial
para incluir un código de Dart en un Paquete.

Tipos de Paquetes
Dado que los paquetes Dart son básicamente una pequeña colección de funcionalidades similares, se pueden categorizar
según su funcionalidad.

Paquete de dardos

Código Dart genérico, que se puede utilizar tanto en entornos web como móviles. Por ejemplo, english_words es uno de
esos paquetes que contiene alrededor de 5000 palabras y tiene funciones de utilidad básicas como sustantivos (enumera los
sustantivos en inglés), sílabas (especifica el número de sílabas en una palabra.

Paquete de aleteo

Código Dart genérico, que depende del marco Flutter y solo se puede usar en un entorno móvil. Por ejemplo, fluro es un
enrutador personalizado para flutter. Depende del aleteo
marco de referencia.

103
Machine Translated by Google

Aleteo

Complemento de aleteo

Código Dart genérico, que depende del marco Flutter, así como del código de la plataforma subyacente (Android SDK o
iOS SDK). Por ejemplo, la cámara es un complemento para interactuar con la cámara del dispositivo. Depende del marco
Flutter, así como del marco subyacente para obtener acceso a la cámara.

Usar un paquete Dart


Los paquetes Dart se alojan y publican en el servidor en vivo, https://pub.dartlang.org.
Además, Flutter proporciona una herramienta simple, pub para administrar paquetes Dart en la aplicación. Los pasos
necesarios para usar como paquete son los siguientes:

• Incluya el nombre del paquete y la versión necesaria en pubspec.yaml como se muestra


debajo:

dependencias:
palabras_en_inglés: ^3.1.5

• El número de versión más reciente se puede encontrar consultando el servidor en línea.

• Instale el paquete en la aplicación usando el siguiente comando:

paquetes flutter obtener

• Durante el desarrollo en Android Studio, Android Studio detecta cualquier cambio en pubspec.yaml y muestra una
alerta de paquete de Android Studio para el desarrollador, como se muestra a continuación:

• Los paquetes Dart se pueden instalar o actualizar en Android Studio usando las opciones del menú.

• Importe el archivo necesario usando el comando que se muestra a continuación y comience a trabajar:

import 'paquete:english_words/english_words.dart';

• Utilice cualquier método disponible en el paquete,

sustantivos.tomar(50).forEach(imprimir);

• Aquí, hemos utilizado la función de sustantivos para obtener e imprimir las 50 palabras principales.

Desarrollar un paquete de complementos de Flutter

Desarrollar un complemento de Flutter es similar a desarrollar una aplicación Dart o un paquete Dart. La única excepción
es que el complemento utilizará la API del sistema (Android o iOS) para obtener la funcionalidad específica de la plataforma
requerida.

Como ya aprendimos cómo acceder al código de la plataforma en los capítulos anteriores, desarrollemos un complemento
simple, my_browser para comprender el proceso de desarrollo del complemento. El

104
Machine Translated by Google

Aleteo

La funcionalidad del complemento my_browser es permitir que la aplicación abra el sitio web dado en el navegador
específico de la plataforma.

• Inicie el estudio de Android

• Haga clic en Archivo -> Nuevo proyecto Flutter y seleccione la opción Complemento Flutter.

• Puede ver una ventana de selección del complemento Flutter como se muestra aquí:

• Ingrese my_browser como nombre del proyecto y haga clic en Siguiente.

105
Machine Translated by Google

Aleteo

• Ingrese el nombre del complemento y otros detalles en la ventana como se muestra aquí:

• Ingrese el dominio de la empresa, flutterplugins.tutorialspoint.com en la ventana que se muestra a continuación


y luego haga clic en Finalizar. Generará un código de inicio para desarrollar nuestro nuevo complemento.

106
Machine Translated by Google

Aleteo

• Abra el archivo my_browser.dart y escriba un método, openBrowser para invocar la plataforma


método openBrowser específico.

Future<void> openBrowser(String urlString) asíncrono {


intente
{resultado int final = esperar _channel.invokeMethod('openBrowser', <String,
String>{
'url': urlString });

} en la captura PlatformException (e) {


// Imposible abrir el navegador print(e);

}
}

• Abra el archivo MyBrowserPlugin.java e importe las siguientes clases:

importar android.app.Actividad;
importar android.content.Intent; importar
android.net.Uri; importar android.os.Bundle;

• Aquí, tenemos que importar la biblioteca requerida para abrir un navegador desde Android.

• Agregar nueva variable privada mRegistrar de tipo Registrar en la clase MyBrowserPlugin.

Registrador final privado mRegistrar;

• Aquí, Registrar se usa para obtener información de contexto del código de invocación.

• Agregar un constructor para configurar Registrar en la clase MyBrowserPlugin.

myBrowserPlugin privado (Registrar registrador)


{ this.mRegistrar = registrador; }

• Cambie registerWith para incluir nuestro nuevo constructor en la clase MyBrowserPlugin.

public static void registerWith(Registrar registrador) { canal


MethodChannel final = new MethodChannel(registrar.messenger(),
"mi_navegador");
Instancia de MyBrowserPlugin = new MyBrowserPlugin(registrador);
channel.setMethodCallHandler(instancia);
}

• Cambie onMethodCall para incluir el método openBrowser en la clase MyBrowserPlugin.

@Override
public void onMethodCall (llamada a MethodCall, resultado de resultado) {

Cadena url = llamada.argumento("url");

107
Machine Translated by Google

Aleteo

if (call.method.equals("getPlatformVersion"))
{ result.success("Android " + android.os.Build.VERSION.RELEASE); } else if
(call.method.equals("openBrowser")) {
openBrowser(llamada, resultado, url); }
else { resultado.noImplementado();

}
}

• Escriba el método openBrowser específico de la plataforma para acceder al navegador en


Clase MyBrowserPlugin:

private void openBrowser (MethodCall call, Result result, String url) {


Actividad actividad = mRegistrar.actividad(); if
(actividad == nulo) { resultado.error
("ACTIVIDAD_NO_DISPONIBLE", "El navegador no se puede abrir sin actividad
en primer plano", nulo); regreso;

Intención intención = nueva Intención (Intent.ACTION_VIEW);


intent.setData(Uri.parse(url));

actividad.startActivity(intención);
resultado.éxito((Objeto) verdadero);
}

• El código fuente completo del complemento my_browser es el siguiente:

mi_navegador.dart

importar 'dardo: asíncrono';

import 'paquete: flutter/services.dart';

clase Mi Navegador {
static const MethodChannel _channel = const
MethodChannel('my_browser');

Futuro estático <String> obtener versión de plataforma asíncrona {


versión final de String = await
_channel.invokeMethod('getPlatformVersion'); versión de
retorno;
}

Future<void> openBrowser(String urlString) asíncrono {


intente
{resultado int final = esperar _channel.invokeMethod('openBrowser',
<Cadena,
Cadena>{ 'url':
urlString });
} en la captura PlatformException (e) {
// Imposible abrir el navegador print(e);

108
Machine Translated by Google

Aleteo

}
}

MiBrowserPlugin.java

paquete com.tutorialspoint.flutterplugins.my_browser;

importar io.flutter.plugin.common.MethodCall; importar


io.flutter.plugin.common.MethodChannel; importar
io.flutter.plugin.common.MethodChannel.MethodCallHandler; importar
io.flutter.plugin.common.MethodChannel.Result; importar
io.flutter.plugin.common.PluginRegistry.Registrar;

importar android.app.Actividad;
importar android.content.Intent; importar
android.net.Uri; importar
android.os.Bundle;

/** MyBrowserPlugin */ clase


pública MyBrowserPlugin implementa MethodCallHandler {
Registrador final privado mRegistrar;

myBrowserPlugin privado (Registrar registrador)


{ this.mRegistrar = registrador;
}

/** Registro del complemento. */


public static void registerWith(Registrar registrador) { canal
MethodChannel final = new MethodChannel(registrar.messenger(), "my_browser"); Instancia
de MyBrowserPlugin = new MyBrowserPlugin(registrador); channel.setMethodCallHandler(instancia);

@Override
public void onMethodCall (llamada a MethodCall, resultado de resultado) {
Cadena url = llamada.argumento("url");

if (call.method.equals("getPlatformVersion"))
{ result.success("Android " + android.os.Build.VERSION.RELEASE); } else if
(call.method.equals("openBrowser")) {
openBrowser(llamada, resultado, url); }
else { resultado.noImplementado();

}
}

private void openBrowser (MethodCall call, Result result, String url) {


Actividad actividad = mRegistrar.actividad(); si (actividad
== nulo) {
result.error("ACTIVITY_NOT_AVAILABLE", "No se puede abrir el navegador
sin actividad en primer plano", null); return;

109
Machine Translated by Google

Aleteo

Intención intención = nueva Intención (Intent.ACTION_VIEW);


intent.setData(Uri.parse(url));

actividad.startActivity(intención);
resultado.éxito((Objeto) verdadero);
}
}

• Cree un nuevo proyecto, my_browser_plugin_test para probar nuestro complemento recién creado.

• Abra pubspec.yaml y configure my_browser como una dependencia del complemento:

dependencias:
aleteo:
SDK: aleteo

mi_navegador:
ruta: ../mi_navegador

• Android Studio alertará que pubspec.yaml se actualizó como se muestra en la alerta del paquete de
Android Studio que se muestra a continuación:

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo
configurará correctamente para la aplicación.

• Abra main.dart e incluya el complemento my_browser como se muestra a continuación:

import 'paquete:mi_navegador/mi_navegador.dart';

• Llame a la función openBrowser desde el complemento my_browser como se muestra a continuación:

onPressed: () => MyBrowser().openBrowser("https://flutter.dev"),

• El código completo del main.dart es el siguiente:

importar 'paquete: flutter/material.dart'; import


'paquete:mi_navegador/mi_navegador.dart';

void main() => runApp(MiAplicación());

clase MyApp extiende StatelessWidget { @override

Compilación del widget (contexto BuildContext) {


return MaterialApp(título:
'Flutter Demo', theme:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(title: 'Flutter Demo
Home Page'), );

110
Machine Translated by Google

Aleteo

}
}

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title:
Text(this.title), ), body: Center( child:
RaisedButton( child: Text('Open
Browser'), onPressed: () = >
MiNavegador().openBrowser("https://
flutter.dev"), ), ), );

}
}

111
Machine Translated by Google

Aleteo

• Ejecute la aplicación y haga clic en el botón Abrir navegador y vea que se inicia el navegador. Puede ver una
aplicación de navegador: página de inicio como se muestra en la captura de pantalla que se muestra a
continuación:

112
Machine Translated by Google

Aleteo

• Puede ver una aplicación de navegador: la pantalla del navegador como se muestra en la captura de pantalla que se muestra
debajo:

113
Machine Translated by Google

Aleteo
14. Flutter: acceso a la API REST

Flutter proporciona un paquete http para consumir recursos HTTP. http es una biblioteca basada en el futuro y
utiliza funciones de espera y asíncronas. Proporciona muchos métodos de alto nivel y simplifica el desarrollo de
aplicaciones móviles basadas en REST.

Conceptos básicos
El paquete http proporciona una clase de alto nivel y http para realizar solicitudes web.

• La clase http proporciona funcionalidad para realizar todo tipo de solicitudes HTTP.

• Los métodos http aceptan una URL e información adicional a través de Dart Map (datos de publicación,
encabezados adicionales, etc.). Solicita al servidor y recopila la respuesta en un patrón asíncrono/en
espera. Por ejemplo, el siguiente código lee los datos de la URL especificada y los imprime en la
consola.

imprimir(esperar http.read('https://flutter.dev/'));

Algunos de los métodos principales son los siguientes:

• leer: solicita la URL especificada a través del método GET y devuelve la respuesta como Future<String>

• get: solicite la URL especificada a través del método GET y devuelva la respuesta como Future<Response>.
Respuesta es una clase que contiene la información de respuesta.

• post: solicite la URL especificada a través del método POST publicando los datos proporcionados y
devuelva la respuesta como Future<Response>

• put: solicita la URL especificada a través del método PUT y devuelve la respuesta
como Futuro<Respuesta>

• head: solicita la URL especificada a través del método HEAD y devuelve el


respuesta como Future<Response>

• eliminar: solicita la URL especificada a través del método DELETE y devuelve el


respuesta como Future<Response>

http también proporciona una clase de cliente HTTP más estándar, client. el cliente admite una conexión
persistente. Será útil cuando se realicen muchas solicitudes a un servidor en particular. Debe cerrarse
correctamente utilizando el método de cierre. De lo contrario, es similar a la clase http. El código de ejemplo es
el siguiente:

var cliente = nuevo http.Cliente();


tratar {
imprimir(esperar cliente.get('https://flutter.dev/'));
} por fin {
cliente.cerrar();
}

114
Machine Translated by Google

Aleteo

Accediendo a ProductserviceAPI
Vamos a crear una aplicación simple para obtener datos de productos de un servidor web y luego mostrar los
productos usando ListView.

• Cree una nueva aplicación Flutter en Android Studio, product_rest_app

• Reemplace el código de inicio predeterminado (main.dart) con nuestro código product_nav_app .

• Copie la carpeta de activos de product_nav_app a product_rest_app y agregue activos


dentro del archivo pubspec.yaml

aleteo:

activos:
- assets/appimages/floppy.png - assets/
appimages/iphone.png - assets/
appimages/laptop.png - assets/
appimages/pendrive.png - assets/
appimages/pixel.png - assets/appimages/
tablet.png

• Configure el paquete http en el archivo pubspec.yaml como se muestra a continuación:

dependencias:
http: ^0.12.0+2

• Aquí, usaremos la última versión del paquete http. Android Studio enviará una alerta de paquete de que se
actualizó pubspec.yaml.

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo
configurará correctamente para la aplicación.

• Importar paquete http en el archivo main.dart:

importar 'dardo: asíncrono';


importar 'dardo: convertir';
importar 'paquete:http/http.dart' como http;

• Cree un nuevo archivo JSON, products.json con la información del producto como se muestra a continuación:

[
{
"name": "iPhone",
"description": "iPhone es el teléfono con estilo de todos los tiempos",
"price": 1000, "image": "iphone.png"

},
{
"name": "Pixel",
"description": "Pixel es el teléfono con más funciones", "price": 800,
"image": "pixel.png"

115
Machine Translated by Google

Aleteo

},
{
"name": "Laptop",
"description": "Laptop es la herramienta de desarrollo más productiva", "price": 2000,
"image": "laptop.png"

},
{
"name": "Tablet",
"description": "Tablet es el dispositivo más útil para reuniones", "price": 1500, "image": "tablet.png"

},
{
"name": "Pendrive",
"description": "Pendrive es un medio de almacenamiento útil", "price": 100,
"image": "pendrive.png"

},
{
"name": "Unidad de disquete",
"descripción": "La unidad de disquete es un medio de almacenamiento de rescate útil",
"price": 20, "image": "floppy.png"

}
]

• Cree una nueva carpeta, JSONWebServer y coloque el archivo JSON, products.json.

• Ejecute cualquier servidor web con JSONWebServer como su directorio raíz y obtenga su ruta web.
Por ejemplo, http://192.168.184.1:8000/products.json. Podemos usar cualquier servidor web como apache, nginx,
etc.,

• La forma más sencilla es instalar una aplicación de servidor http basada en nodos. Siga los pasos dados
a continuación para instalar y ejecutar la aplicación del servidor http.

• Instale la aplicación Nodejs (https://nodejs.org/en/)


• Ir a la carpeta JSONWebServer.

cd /ruta/a/JSONWebServer

• Instale el paquete del servidor http usando npm

npm install -g servidor http

• Ahora, ejecute el servidor.

servidor http. -pag 8000

Iniciando servidor http, sirviendo .


Disponible en:
http://192.168.99.1:8000 http://
192.168.1.2:8000

116
Machine Translated by Google

Aleteo

http://127.0.0.1:8000
Presione CTRL-C para detener el servidor

• Cree un nuevo archivo, Product.dart en la carpeta lib y mueva la clase Product a él.

• Escriba un constructor de fábrica en la clase Product, Product.fromMap para convertir Map de datos
asignados en el objeto Product. Normalmente, el archivo JSON se convertirá en un objeto Dart
Map y luego se convertirá en un objeto relevante (Producto)

Factory Product.fromJson(Map<String, dynamic> data) {


return

Product(datos['nombre'],
datos['descripción'],
datos['precio'],
datos['imagen'], );
}

• El código completo del Product.dart es el siguiente:

clase Producto
{ cadena final nombre;
descripción final de la cadena;
precio internacional final; imagen
de cadena final;

Producto(este.nombre, esta.descripción, este.precio, esta.imagen);

Factory Product.fromMap(Map<String, dynamic> json) { return


Product( json['name'], json['description'], json['price'], json['image'], );

}
}

• Escriba dos métodos, parseProducts y fetchProducts, en la clase principal para obtener y cargar la
información del producto del servidor web en el objeto List<Producto>.

List<Product> parseProducts(String responseBody) { final parsed


= json.decode(responseBody).cast<Map<String, dynamic>>(); return
analizado.map<Producto>((json) => Producto.fromJson(json)).toList();
}

Future<List<Product>> fetchProducts() asíncrono {


respuesta final = espera
http.get('http://192.168.1.2:8000/products.json');

if (respuesta.statusCode == 200) {
return parseProducts(respuesta.cuerpo); } else
{ throw Exception('No se pueden obtener
productos de la API REST');

117
Machine Translated by Google

Aleteo

}
}

• Tenga en cuenta los siguientes puntos aquí:

• El futuro se utiliza para la carga diferida de la información del producto. Lazy loading es un concepto
para diferir la ejecución del código hasta que sea necesario.

• http.get se utiliza para obtener los datos de Internet.

• json.decode se usa para decodificar los datos JSON en el objeto Dart Map. Una vez que se
decodifican los datos JSON, se convertirán en List<Product> usando fromMap de la clase Product.

• En la clase MyApp, agregue una nueva variable miembro, productos de tipo Future<Product> e
inclúyala en el constructor.

class MyApp extiende StatelessWidget {


Productos finales de Future<List<Product>>;

MyApp({Clave clave, este.productos}) : super(clave: clave);

...

• En la clase MyHomePage, agregue nuevos productos de variables miembro de tipo Future<Product>


e inclúyalos en el constructor. Además, elimine la variable de elementos y su método relevante,
llame al método getProducts. Colocando la variable de productos en el constructor. Permitirá
obtener los productos de Internet solo una vez cuando se inicie la aplicación por primera vez.

clase MyHomePage extiende StatelessWidget {


título final de la cadena;
Productos finales de Future<List<Product>>;

MyHomePage({Clave clave, este.título, este.productos}) : super(clave: clave);


...

• Cambie la opción de inicio (MyHomePage) en el método de compilación del widget MyApp a


adaptarse a los cambios anteriores:

casa: MiPáginaInicio(
título: 'Página de inicio de demostración de navegación del producto',
productos: productos),

• Cambiar la función principal para incluir argumentos Future<Product>:

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

• Cree un nuevo widget, ProductBoxList para crear la lista de productos en la página de inicio.

clase ProductBoxList extiende StatelessWidget {


Lista final <Producto> elementos;

118
Machine Translated by Google

Aleteo

ProductBoxList({Clave clave, this.items});

@override
Widget build (contexto BuildContext) { return
ListView.builder(
itemCount: items.length,
itemBuilder: (contexto, índice) { return
GestureDetector( child: ProductBox(item:
items[index]), onTap: () { Navigator.push( context,
MaterialPageRoute(

constructor: (contexto) => ProductPage(item: items[index]), ), ); }, ); }, );

}
}

Tenga en cuenta que usamos el mismo concepto que se usa en la aplicación de navegación para listar el
producto, excepto que está diseñado como un widget separado al pasar productos (objeto) de tipo List<Product>.

• Finalmente, modifique el método de compilación del widget MyHomePage para obtener la


información del producto usando la opción Futuro en lugar de la llamada al método normal.

Compilación del widget (contexto BuildContext) {


return Scaffold(appBar:
AppBar(título: Texto("Navegación del producto")), cuerpo: Centro(

child: FutureBuilder<List<Product>>( future:


products, builder: (context, snapshot) {

if (instantánea.hasError) print(instantánea.error);

devolver instantánea.hasData ?
ProductBoxList(items:
snapshot.data) // devuelve el widget ListView
: Centro (hijo: CircularProgressIndicator());

}, ), ));
}

• Tenga en cuenta que usamos el widget FutureBuilder para representar el widget. FuturoConstructor
intentará obtener los datos de su propiedad futura (de tipo Future<List<Product>>).
Si la propiedad futura devuelve datos, representará el widget utilizando ProductBoxList; de lo
contrario, arrojará un error.

119
Machine Translated by Google

Aleteo

• El código completo del main.dart es el siguiente:

importar 'paquete: flutter/material.dart';

importar 'dardo:
asíncrono'; importar 'dardo:
convertir'; importar 'paquete:http/http.dart' como http;

importar 'Producto.dardo';

void main() => runApp(MyApp(products: fetchProducts()));

List<Product> parseProducts(String responseBody) { final parsed


= json.decode(responseBody).cast<Map<String, dynamic>>(); return
analizado.map<Producto>((json) => Producto.fromMap(json)).toList();
}

Future<List<Product>> fetchProducts() asíncrono {


respuesta final = espera
http.get('http://192.168.1.2:8000/products.json');

if (respuesta.statusCode == 200) {
return parseProducts(respuesta.cuerpo); }
else { throw Exception('No se pueden obtener
productos de la API REST');
}
}

class MyApp extiende StatelessWidget {


Productos finales de Future<List<Product>>;

MyApp({Clave clave, este.productos}) : super(clave: clave);

// Este widget es la raíz de su aplicación. @override Widget


compilación (contexto BuildContext) {

return MaterialApp(título:
'Flutter Demo', theme:
ThemeData(primarySwatch:
Colors.blue, ), home:
MyHomePage(título: 'Product
Navigation demo home page',
products: products), );

}
}

class MyHomePage extiende StatelessWidget { final


String title; Productos finales de Future<List<Product>>;

MyHomePage({Clave clave, este.título, este.productos}) : super(clave: clave);

// elementos finales = Product.getProducts();

120
Machine Translated by Google

Aleteo

@anular
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title: Text("Product
Navigation")), body: Center( child:
FutureBuilder<List<Product>>( future: products, builder: (context,
snapshot) {

if (instantánea.hasError) print(instantánea.error);

devolver instantánea.hasData ?
ProductBoxList(items:
snapshot.data) // devuelve el widget ListView
: Centro (hijo: CircularProgressIndicator());

}, ), ));
}
}

clase ProductBoxList extiende StatelessWidget {


Lista final <Producto> elementos;

ProductBoxList({Clave clave, this.items});

@override
Widget build (contexto BuildContext) { return
ListView.builder(
itemCount: items.length,
itemBuilder: (contexto, índice) { return
GestureDetector( child:
ProductBox(item: items[index]), onTap: ()
{ Navigator.push( context, MaterialPageRoute(

constructor: (contexto) => ProductPage(item: items[index]), ), ); }, ); }, );

}
}

clase ProductPage extiende StatelessWidget {


ProductPage({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

@anular
Creación de widgets (contexto BuildContext)
{ return Scaffold (

121
Machine Translated by Google

Aleteo

appBar:
AppBar( título:
Texto(este.elemento.nombre), ),
cuerpo: Centro( hijo: Contenedor( relleno:
EdgeInsets.all(0), hijo:
Columna( mainAxisAlignment:
MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start, children:
<Widget>[ Image.asset("assets/appimages/" +
this.item.image), Expanded( child: Container( padding:
EdgeInsets.all(5), child: Column( mainAxisAlignment:
MainAxisAlignment.spaceEvenly,

niños: <Widget>[
Text(this.item.name, style:
TextStyle(fontWeight:
Peso de la fuente.negrita)),
Text(this.item.description),
Text("Precio: " + this.item.price.toString()), RatingBox(), ], )))

]),

), ), );

}
}

clase RatingBox extiende StatefulWidget { @override

_RatingBoxState createState() => _RatingBoxState();


}

class _RatingBoxState extiende Estado<RatingBox> {


int _calificación = 0;

void _setRatingAsOne()
{ setState(() { _rating = 1; });

void _setRatingAsTwo()
{ setState(() { _rating = 2; });

void _setRatingAsThree()
{ setState(() {

122
Machine Translated by Google

Aleteo

_puntuación =
3; });
}

Compilación del widget (contexto BuildContext) {


doble _tamaño = 20;
imprimir (_calificación);

return
Row( mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.end, mainAxisSize:
MainAxisSize.max, children: <Widget>[ Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 1 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border, size:
_size, )), color:
Colors.red[500], onPressed:
_setRatingAsOne, iconSize:
_size, ), ), Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 2 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)
:
Icon( Icons.star_border, size:
_size, )), color:
Colors.red[500], onPressed:
_setRatingAsTwo, iconSize:
_size, ), ), Container( padding:
EdgeInsets.all(0), child: IconButton(

icono: (_calificación >= 3 ?


Icono( Iconos.estrella,
tamaño:
_tamaño,
)

123
Machine Translated by Google

Aleteo

:
Icon( Icons.star_border,
size: _size, )), color:
Colors.red[500],
onPressed: _setRatingAsThree,
iconSize: _size, ), ), ], );

}
}

class ProductBox extiende StatelessWidget {


ProductBox({Clave clave, este.elemento}) : super(clave: clave);

artículo del producto final;

Creación de widgets (contexto BuildContext)


{ return Container ( relleno: EdgeInsets.all
(2), altura: 140, niño: Tarjeta ( niño:
Fila (

mainAxisAlignment: MainAxisAlignment.spaceEvenly, children:


<Widget>[ Image.asset("assets/appimages/" + this.item.image),
Expanded( child: Container( padding: EdgeInsets.all(5), child:
Column( mainAxisAlignment : MainAxisAlignment.spaceEvenly,
hijos: <Widget>[

Text(this.item.name,
style: TextStyle(fontWeight:
Peso de la fuente.negrita)),
Text(this.item.description),
Text("Precio: " + this.item.price.toString()), RatingBox(), ], )))

]),
));
}
}

Finalmente ejecute la aplicación para ver el resultado. Será igual que nuestro ejemplo de Navegación ,
excepto que los datos son de Internet en lugar de datos estáticos locales ingresados al codificar la
aplicación.

124
Machine Translated by Google

Aleteo
15. Flutter – Conceptos de base de datos

Flutter proporciona muchos paquetes avanzados para trabajar con bases de datos. Los paquetes más importantes son:

• sqflite: se utiliza para acceder y manipular la base de datos SQLite, y

• firebase_database: se utiliza para acceder y manipular la base de datos NoSQL alojada en la nube
de Google.

En este capítulo, analicemos cada uno de ellos en detalle.

SQLite
La base de datos SQLite es el motor de base de datos integrado basado en SQL estándar y de facto. Es un motor de
base de datos pequeño y probado en el tiempo. El paquete sqflite proporciona una gran cantidad de funciones para
trabajar de manera eficiente con la base de datos SQLite. Proporciona métodos estándar para manipular el motor de
base de datos SQLite. La funcionalidad principal proporcionada por el paquete sqflite es la siguiente:

• Crear/Abrir (método openDatabase) una base de datos SQLite.

• Ejecutar declaración SQL (método de ejecución) contra la base de datos SQLite.

• Métodos de consulta avanzados (método de consulta) para reducir al código requerido para consultar y
obtener información de la base de datos SQLite.

Vamos a crear una aplicación de producto para almacenar y obtener información de productos de un motor de base de
datos SQLite estándar usando el paquete sqflite y entender el concepto detrás de la base de datos SQLite y el paquete
sqflite.

• Cree una nueva aplicación Flutter en Android Studio, product_sqlite_app

• Reemplace el código de inicio predeterminado (main.dart) con nuestro código product_rest_app .

• Copie la carpeta de activos de product_nav_app a product_rest_app y agregue activos


dentro del archivo *pubspec.yaml`

aleteo:

activos:
- activos/imágenes de la aplicación/disquete.png
- activos/imágenes de la aplicación/iphone.png
- activos/imágenes de la aplicación/portátil.png
- activos/imágenes de la aplicación/pendrive.png
- activos/imágenes de la aplicación/pixel.png
- activos/imágenes de la aplicación/tableta.png

• Configure el paquete sqflite en el archivo pubspec.yaml como se muestra a continuación:

dependencias:
sqflite: cualquiera

125
Machine Translated by Google

Aleteo

Use el número de versión más reciente de sqflite en lugar de cualquier

• Configure el paquete path_provider en el archivo pubspec.yaml como se muestra a continuación:

dependencias:
ruta_proveedor: cualquiera

• Aquí, el paquete path_provider se usa para obtener la ruta de la carpeta temporal del sistema y la ruta de la
aplicación. Use el número de versión más reciente de sqflite en lugar de cualquiera.

• Android Studio alertará que pubspec.yaml está actualizado.

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo configurará
correctamente para la aplicación.

• En la base de datos, necesitamos la clave principal, la identificación como campo adicional junto con las
propiedades del Producto como el nombre, el precio, etc. Por lo tanto, agregue la propiedad de identificación
en la clase Producto. Además, agregue un nuevo método, toMap para convertir un objeto de producto en un
objeto de mapa. fromMap y toMap se utilizan para serializar y deserializar el objeto Producto y se utiliza en
métodos de manipulación de bases de datos.

clase Producto { id
int final; nombre
final de la cadena;
descripción final de la cadena;
precio internacional final; imagen
de cadena final;

columnas finales estáticas = ["id", "nombre", "descripción", "precio", "imagen"];

Producto(este.id, este.nombre, esta.descripción, este.precio, esta.imagen);

Factory Product.fromMap(Map<String, dynamic> data) { return


Product( data['id'], data['name'], data['description'], data['price'],
data['image' ], );

Map<String, dynamic> toMap() => { "id": id,


"nombre": nombre, "descripción":
descripción, "precio": precio, "imagen":
imagen };

• Cree un nuevo archivo, Database.dart en la carpeta lib para escribir la funcionalidad relacionada con SQLite .

• Importar declaración de importación necesaria en Database.dart

126
Machine Translated by Google

Aleteo

importar 'dardo: asíncrono';


importar 'dardo:io'; import
'paquete:ruta/ruta.dart';

importar 'paquete: ruta_proveedor/ruta_proveedor.dart'; importar 'paquete:


sqflite/sqflite.dart';

importar 'Producto.dardo';

• Tenga en cuenta los siguientes puntos aquí:

• async se utiliza para escribir métodos asincrónicos.

• io se utiliza para acceder a archivos y directorios.

• ruta se utiliza para acceder a la función de la utilidad dart core relacionada con las rutas de archivo.

• path_provider se utiliza para obtener la ruta temporal y de la aplicación.

• sqflite se usa para manipular la base de datos SQLite.

• Crear una nueva clase SQLiteDbProvider

• Declare un objeto SQLiteDbProvider estático basado en singleton como se especifica a continuación:

clase SQLiteDbProvider
{ SQLiteDbProvider._();

SQLiteDbProvider final estático db = SQLiteDbProvider._();

Base de datos estática _base de datos;


}

• Se puede acceder al objeto SQLiteDBProvoider y su método a través de la base de datos estática


variable.

SQLiteDBProvoider.db.<método>

• Crear un método para obtener la base de datos (opción Future) de tipo Future<Database>.
Cree una tabla de productos y cargue los datos iniciales durante la creación de la propia base de datos.

Future<Database> get base de datos asíncrona { if


(_database != null) return _database;

_base de datos = espera initDB();


devolver _base de datos;
}

initDB() asíncrono {
Directorio documentosDirectorio = esperar
getApplicationDocumentsDirectory(); String path =
join(documentsDirectory.path, "ProductDB.db"); regresar esperar openDatabase (ruta,
versión: 1,

127
Machine Translated by Google

Aleteo

onOpen: (db) {},


onCreate: (Base de datos db, versión int) asíncrono {

await db.execute("CREATE TABLE Producto ("


"id CLAVE PRIMARIA ENTERA,"
"nombre TEXTO,"
"texto de descripción,"
"precio ENTERO"
"texto de la imagen"
")");

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[1, "iPhone", "iPhone es el teléfono con más estilo", 1000,
"iphone.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[2, "Pixel", "Pixel es el teléfono con más funciones de la historia", 800,
"píxel.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[3, "Laptop", "Laptop es la herramienta de desarrollo más productiva",
2000, "portátil.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[4, "Tablet", "La computadora portátil es la herramienta de desarrollo más productiva",
1500, "tableta.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[5, "Pendrive", "Pendrive es un medio de almacenamiento útil", 100,
"pendrive.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[6, "Unidad de disquete", "La unidad de disquete es un medio de
almacenamiento de rescate útil", 20, "floppy.png"]);
});
}

• Aquí, hemos utilizado los siguientes métodos:

• getApplicationDocumentsDirectory : devuelve la ruta del directorio de la aplicación

• unirse : se utiliza para crear una ruta específica del sistema. Lo hemos usado para crear una base de datos.

sendero.

128
Machine Translated by Google

Aleteo

• openDatabase : se utiliza para abrir una base de datos SQLite

• onOpen : se utiliza para escribir código al abrir una base de datos

• onCreate : se usa para escribir código mientras se crea una base de datos por primera vez

• db.execute : se utiliza para ejecutar consultas SQL. Acepta una consulta. Si la consulta tiene

marcador de posición (?), luego acepta valores como lista en el segundo argumento.

• Escriba un método para obtener todos los productos en la base de datos:

Future<List<Product>> getAllProducts() asíncrono {


db final = base de datos en espera;

List<Map> resultados = await db.query("Producto", columnas: Producto.columnas, orderBy: "id ASC");

Lista<Producto> productos = nueva Lista();


resultados.forEach((resultado) {
Producto producto = Producto.fromMap(resultado);
productos.add(producto);
});

devolución de productos;
}

• Aquí, hemos hecho lo siguiente:

• Método de consulta utilizado para obtener toda la información del producto. consulta proporciona un acceso directo
para consultar la información de una tabla sin escribir toda la consulta. El método de consulta generará la
consulta adecuada utilizando nuestra entrada como columnas, orderBy, etc.,

• Se usó el método fromMap del producto para obtener detalles del producto mediante un bucle de los resultados.
objeto, que contiene todas las filas de la tabla.

• Escriba un método para obtener un producto específico para id

Future<Producto> getProductById(int id) asíncrono {


db final = base de datos en espera;

var result = await db.query("Producto", donde: "id = ", whereArgs: [id]);

devolver resultado.isNotEmpty ? Product.fromMap(resultado.primero) : Null;


}

• Aquí, hemos usado where y whereArgs para aplicar filtros.

• Crear tres métodos: método de inserción, actualización y eliminación para insertar, actualizar y eliminar productos
de la base de datos

insertar (Producto producto) asíncrono {


db final = base de datos en espera;

var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id FROM


Product");

129
Machine Translated by Google

Aleteo

var id = maxIdResult.first["last_inserted_id"];

var resultado = esperar db.rawInsert(


"INSERTAR en el producto (id, nombre, descripción, precio, imagen)"
"
VALORES (?, ?, ?, ?, ?)",
[id, nombre.del.producto, descripción.del.producto, precio.del.producto,
Imagen del
producto] );

resultado devuelto;
}

actualización (producto del producto) asíncrono


{ db final = base de datos en espera;

var result = await db.update("Producto", product.toMap(), where: "id = ?", whereArgs:


[producto.id]);

resultado devuelto;
}

delete(int id) asíncrono { db final


= base de datos en espera;

db.delete("Producto", donde: "id = ?", whereArgs: [id]);


}

• El código final de Database.dart es el siguiente:

importar 'dardo: asíncrono';


importar 'dardo:io'; import
'paquete:ruta/ruta.dart';

importar 'paquete: ruta_proveedor/ruta_proveedor.dart'; importar 'paquete:


sqflite/sqflite.dart';

importar 'Producto.dardo';

clase SQLiteDbProvider
{ SQLiteDbProvider._();

SQLiteDbProvider final estático db = SQLiteDbProvider._();

Base de datos estática _base de datos;

Future<Database> get base de datos asíncrona { if


(_database != null) return _database;

_base de datos = espera initDB();


devolver _base de datos;
}

initDB() asíncrono {

130
Machine Translated by Google

Aleteo

Directorio documentosDirectorio = esperar


getApplicationDocumentsDirectory();
String path = join(documentsDirectory.path, "ProductDB.db");
volver esperar openDatabase(
sendero,
versión 1,
onOpen: (db) {},
onCreate: (Base de datos db, versión int) asíncrono {

await db.execute("CREATE TABLE Producto ("


"id CLAVE PRIMARIA ENTERA,"
"nombre TEXTO,"
"texto de descripción,"
"precio ENTERO"
"texto de la imagen"
")");

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[1, "iPhone", "iPhone es el teléfono con más estilo", 1000,
"iphone.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[2, "Pixel", "Pixel es el teléfono con más funciones de la historia", 800,
"píxel.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[3, "Laptop", "Laptop es la herramienta de desarrollo más productiva",
2000, "portátil.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[4, "Tablet", "La computadora portátil es la herramienta de desarrollo más productiva",
1500, "tableta.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[5, "Pendrive", "Pendrive es un medio de almacenamiento útil", 100,
"pendrive.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[6, "Unidad de disquete", "La unidad de disquete es un medio de
almacenamiento de rescate útil", 20, "floppy.png"]);
});
}

131
Machine Translated by Google

Aleteo

Future<List<Product>> getAllProducts() asíncrono {


db final = base de datos en espera;

List<Map> resultados = await db.query("Producto", columnas:


Producto.columnas, orderBy: "id ASC");

Lista<Producto> productos = nueva Lista();


resultados.forEach((resultado) { Producto
producto = Producto.fromMap(resultado);
productos.añadir(producto); });

devolución de productos;
}

Future<Producto> getProductById(int id) asíncrono {


db final = base de datos en espera;

var result = await db.query("Producto", donde: "id = ", whereArgs: [id]);

devolver resultado.isNotEmpty ? Product.fromMap(resultado.primero) : Nulo;


}

insertar (Producto producto) asíncrono


{ db final = base de datos en espera;

var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id


FROM Product"); var id = maxIdResult.first["last_inserted_id"];

var resultado = esperar db.rawInsert(


"INSERTAR en el producto (id, nombre, descripción, precio, imagen)"
"
VALORES (?, ?, ?, ?, ?)",
[id, nombre.del.producto, descripción.del.producto, precio.del.producto,
Imagen del
producto] );

resultado devuelto;
}

actualización (producto del producto) asíncrono


{ db final = base de datos en espera;

var result = await db.update("Producto", product.toMap(), where: "id = ?",


whereArgs: [producto.id]);

resultado devuelto;
}

delete(int id) asíncrono { db


final = base de datos en espera;

db.delete("Producto", donde: "id = ?", whereArgs: [id]);

132
Machine Translated by Google

Aleteo

}
}

• Cambiar el método principal para obtener la información del producto.

vacío principal() {
runApp(MyApp(productos: SQLiteDbProvider.db.getAllProducts()));
}

• Aquí, hemos utilizado el método getAllProducts para obtener todos los productos de la base de datos.

• Ejecute la aplicación y vea los resultados. Será similar al ejemplo anterior, Acceder a la API del servicio del
producto, excepto que la información del producto se almacena y se obtiene de la base de datos SQLite
local.

Tienda de fuego en la nube

Firebase es una plataforma de desarrollo de aplicaciones BaaS. Proporciona muchas características para acelerar el
desarrollo de aplicaciones móviles como servicio de autenticación, almacenamiento en la nube, etc. Una de las
características principales de Firebase es Cloud Firestore, una base de datos NoSQL en tiempo real basada en la nube.

Flutter proporciona un paquete especial, cloud_firestore para programar con Cloud Firestore. Vamos a crear
una tienda de productos en línea en Cloud Firestore y crear una aplicación para acceder a la tienda de
productos.

• Cree una nueva aplicación Flutter en Android Studio, product_firebase_app

• Reemplace el código de inicio predeterminado (main.dart) con nuestro código product_rest_app .

• Copie el archivo Product.dart de product_rest_app en la carpeta lib.

producto de clase {
nombre final de la cadena;
descripción final de la cadena;
precio internacional final;
imagen de cadena final;

Producto(este.nombre, esta.descripción, este.precio, esta.imagen);

fábrica Product.fromMap(Map<String, dynamic> json) {


producto devuelto (
json['nombre'],
json['descripción'],
json['precio'],
json['imagen'],
);
}
}

• Copie la carpeta de activos de product_rest_app a product_firebase_app y agregue activos dentro del


archivo pubspec.yaml

aleteo:

133
Machine Translated by Google

Aleteo

activos:
- activos/imágenes de la aplicación/disquete.png
- activos/imágenes de la aplicación/iphone.png
- activos/imágenes de la aplicación/portátil.png
- activos/imágenes de la aplicación/pendrive.png
- activos/imágenes de la aplicación/pixel.png
- activos/imágenes de la aplicación/tableta.png

• Configure el paquete cloud_firestore en el archivo pubspec.yaml como se muestra a continuación:

dependencias:
tienda_de_fuego_en_la_nube: ^0.9.13+1

• Aquí, use la última versión del paquete cloud_firestore.

• Android Studio alertará que pubspec.yaml se actualiza como se muestra aquí:

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo configurará correctamente
para la aplicación.

• Cree un proyecto en Firebase siguiendo los siguientes pasos:

• Crear a base de fuego cuenta por seleccionando Gratis plan en


https://firebase.google.com/pricing/

• Una vez que se crea la cuenta de Firebase, se redirigirá a la página de descripción general del proyecto. Enumera todos
los proyectos basados en Firebase y proporciona una opción para crear un nuevo proyecto.

• Haga clic en Agregar proyecto y se abrirá una página de creación de proyectos.

• Ingrese la base de datos de la aplicación de productos como nombre del proyecto y haga clic en la opción Crear proyecto.

• Vaya a *Consola de Firebase.

• Haga clic en Resumen del proyecto. Abre la página de descripción general del proyecto.

• Haga clic en el icono de Android. Se abrirá la configuración del proyecto específica para el desarrollo de Android.

• Ingrese el nombre del paquete de Android, com.tutorialspoint.flutterapp.product_firebase_app

• Haga clic en Registrar aplicación. Genera un archivo de configuración del proyecto, google_service.json

• Descargue google_service.json y luego muévalo al directorio android/app del proyecto. Este archivo es la conexión entre
nuestra aplicación y Firebase.

• Abra android/app/build.gradle e incluya el siguiente código:

aplicar complemento: 'com.google.gms.google-services'

• Abra android/build.gradle e incluya la siguiente configuración:

134
Machine Translated by Google

Aleteo

buildscript
{ repositorios { // ...

dependencias
{ // ...
classpath 'com.google.gms:google-services:3.2.1' // nuevo
}
}

Aquí, el complemento y la ruta de clase se utilizan con el fin de leer el archivo


google_service.json.

• Abra android/app/build.gradle e incluya también el siguiente código.

android
{ configuración predeterminada {
...
multiDexEnabled verdadero
}
...
}

dependencias {
...
compilar 'com.android.support: multidex:1.0.3'
}

Esta dependencia permite que la aplicación de Android use la funcionalidad de dex múltiple.

• Siga los pasos restantes en Firebase Console o simplemente sáltelos.

• Cree una tienda de productos en el proyecto recién creado siguiendo los siguientes pasos:

• Vaya a la consola de Firebase.

• Abra el proyecto recién creado.

• Haga clic en la opción Base de datos en el menú de la izquierda.

• Haga clic en la opción Crear base de datos.

• Haga clic en Iniciar en modo de prueba y luego Habilitar

• Haz clic en Agregar colección. Ingrese el producto como nombre de la colección y luego haga clic en Siguiente.

• Ingrese la información del producto de muestra como se muestra en la imagen aquí:

135
Machine Translated by Google

Aleteo

• Agregar información adicional del producto usando las opciones Agregar documento .

• Abra el archivo main.dart e importe el archivo del complemento Cloud Firestore y elimine el paquete http.

importar 'paquete:cloud_firestore/cloud_firestore.dart';

• Eliminar parseProducts y actualizar fetchProducts para obtener productos de la nube


Firestore en lugar de la API de servicio del producto

Flujo <Instantánea de consulta> fetchProducts() {


return Firestore.instance.collection('producto').snapshots();
}

• Aquí, el método Firestore.instance.collection se usa para acceder a la colección de productos


disponible en la tienda en la nube. Firestore.instance.collection ofrece muchas opciones para filtrar la
colección y obtener los documentos necesarios. Pero no hemos aplicado ningún filtro para obtener toda
la información del producto.

• Cloud Firestore proporciona la colección a través del concepto Dart Stream y, por lo tanto, modifica el tipo de
productos en el widget MyApp y MyHomePage de Future<list<Product>> a Stream<QuerySnapshot>.

136
Machine Translated by Google

Aleteo

• Cambie el método de compilación del widget MyHomePage para usar StreamBuilder en lugar de
Constructor de Futuro.

@anular
Widget build(BuildContext context) { return
Scaffold(appBar: AppBar(title: Text("Product
Navigation")), body: Center( child:
StreamBuilder<QuerySnapshot>( stream: products, builder:
(context, snapshot) {

if (instantánea.hasError) print(instantánea.error);

if(snapshot.hasData)
{ List<DocumentSnapshot> documentos = snapshot.data.documents;
List<Producto> artículos = List<Producto>();

for(var i = 0; i < documentos.longitud; i++)


{ DocumentSnapshot documento = documentos[i];

items.add(Producto.fromMap(documento.datos));
}

volver ProductBoxList(artículos: artículos); }


else { return Center(child:
CircularProgressIndicator());

} }, ), ));

• Aquí, hemos obtenido la información del producto como tipo List<DocumentSnapshot>.


Dado que nuestro widget, ProductBoxList, no es compatible con los documentos, hemos convertido
los documentos en el tipo List<Product> y los hemos utilizado más.
• Finalmente, ejecute la aplicación y vea el resultado. Dado que hemos utilizado la misma información
del producto que la de la aplicación SQLite y solo hemos cambiado el medio de almacenamiento,
la aplicación resultante se ve idéntica a la aplicación de la aplicación SQLite .

137
Machine Translated by Google

16. Flutter – Internacionalización Aleteo

Hoy en día, las aplicaciones móviles son utilizadas por clientes de diferentes países y, como resultado, se requieren
aplicaciones para mostrar el contenido en diferentes idiomas. Permitir que una aplicación funcione en varios idiomas se
denomina internacionalización de la aplicación.

Para que una aplicación funcione en diferentes idiomas, primero debe encontrar la configuración regional actual del sistema
en el que se ejecuta la aplicación y luego debe mostrar su contenido en esa configuración regional en particular, y este
proceso se denomina Localización.

Flutter framework proporciona tres clases base para la localización y amplias clases de utilidad derivadas de las clases
base para localizar una aplicación.

Las clases base son las siguientes:

• Configuración regional: la configuración regional es una clase utilizada para identificar el idioma del usuario. Por ejemplo, en-us
identifica el inglés americano y se puede crear como:

Locale en_locale = Locale ('en', 'US')

Aquí, el primer argumento es el código de idioma y el segundo argumento es el código de país.


Otro ejemplo de cómo crear una configuración regional en español de Argentina (es-ar) es el siguiente:

Locale es_locale = Locale('es', 'AR')

• Localizaciones: las localizaciones son un widget genérico que se utiliza para establecer la configuración
regional y los recursos localizados de su hijo.

class Localizaciones personalizadas {


CustomLocalizations(this.locale);

local final local;

Localizaciones personalizadas estáticas de (contexto BuildContext) {


return Localizations.of<CustomLocalizations>(context, CustomLocalizations);

Mapa estático<Cadena, Mapa<Cadena, Cadena>> _resources = {


'en': {
'título': 'Demostración',
'mensaje': 'Hola Mundo'
},
'es': {
'title': 'Manifestación',
'message': 'Hola Mundo',
},
};

Cadena obtener título {


return _resources[locale.languageCode]['title'];
}

138
Machine Translated by Google

Aleteo

Cadena obtener mensaje {


return _resources[locale.languageCode]['mensaje'];
}
}

• Aquí, CustomLocalizations es una nueva clase personalizada creada específicamente para obtener cierto
contenido localizado (título y mensaje) para el widget. of utiliza la clase Localizations para devolver una nueva
clase CustomLocalizations.

• LocalizationsDelegate<T>: LocalizationsDelegate<T> es una clase de fábrica a través de la cual se carga el widget


de Localizaciones. Tiene tres métodos reemplazables:

• isSupported: acepta una configuración regional y devuelve si la configuración regional especificada es


apoyado o no.

@anular
bool isSupported(Locale locale) => ['en',
'es'].contains(locale.languageCode);

Aquí, el delegado trabaja solo para en y es locale.

• load: acepta una configuración regional y comienza a cargar los recursos para la configuración regional
especificada.

@anular
Future<CustomLocalizations> load(Locale locale) {
regreso
SynchronousFuture<LocalizacionesPersonalizadas>(LocalizacionesPersonalizadas(locale));
}

Aquí, el método de carga devuelve CustomLocalizations. Las CustomLocalizations devueltas se pueden


usar para obtener valores de título y mensaje tanto en inglés como en español.

• shouldReload: especifica si es necesario volver a cargar CustomLocalizations cuando se reconstruye su widget


de Localizaciones.

@anular
bool shouldReload(CustomLocalizationsDelegate old) => false;

• El código completo de CustomLocalizationDelegate es el siguiente:

clase CustomLocalizationsDelegate extiende


LocalizationsDelegate<CustomLocalizations> {
const CustomLocalizationsDelegate();

@anular
bool es compatible => ['en',
'es'].contains(locale.languageCode);

@anular
Future<CustomLocalizations> load(Locale locale) {
regreso
SynchronousFuture<LocalizacionesPersonalizadas>(LocalizacionesPersonalizadas(locale));

139
Machine Translated by Google

Aleteo

@anular
bool shouldReload(CustomLocalizationsDelegate old) => false;
}

En general, las aplicaciones de Flutter se basan en dos widgets de nivel raíz, MaterialApp o WidgetsApp.
Flutter proporciona localización preparada para ambos widgets y son MaterialLocalizations y
WidgetsLocaliations. Además, Flutter también proporciona delegados para cargar MaterialLocalizations y
GlobalMaterialLocalizations.delegate respectivamente.WidgetsLocaliations y ellos y son
GlobalWidgetsLocalizations.delegate

Vamos a crear una aplicación sencilla habilitada para la internacionalización para probar y comprender el
concepto.

• Crear una nueva aplicación flutter, flutter_localization_app

• Flutter admite la internacionalización mediante el paquete exclusivo de flutter, flutter_localizations. La


idea es separar el contenido localizado del SDK principal.
Abra pubspec.yaml y agregue el siguiente código para habilitar el paquete de internacionalización:

dependencias:
aleteo:
SDK: aleteo
flutter_localizaciones:
SDK: aleteo

• Android Studio mostrará la siguiente alerta de que pubspec.yaml está actualizado.

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo
configurará correctamente para la aplicación.

• Importe el paquete flutter_localizations en main.dart de la siguiente manera:

importar 'paquete: flutter_localizations/flutter_localizations.dart';


importar 'paquete: flutter/foundation.dart' show SynchronousFuture;

• Aquí, el propósito de SynchronousFuture es cargar las localizaciones personalizadas de forma


sincrónica.

• Cree una localización personalizada y su correspondiente delegado como se especifica a continuación:

class Localizaciones personalizadas {


CustomLocalizations(this.locale);

local final local;

Localizaciones personalizadas estáticas de (contexto BuildContext) {


return Localizations.of<CustomLocalizations>(context,
CustomLocalizations);
}

140
Machine Translated by Google

Aleteo

Mapa estático<Cadena, Mapa<Cadena, Cadena>> _resources = {


'es':
{ 'title': 'Demo',
'message': 'Hello World'
},
'es':
{ 'title': 'Manifestación', 'message':
'Hola Mundo',

}, };

String get title { return


_resources[locale.languageCode]['title'];
}

String get message


{ return _resources[locale.languageCode]['message'];
}
}

clase CustomLocalizationsDelegate extiende


LocalizationsDelegate<CustomLocalizations> { const
CustomLocalizationsDelegate();

@override
bool isSupported(Locale locale) => ['en',
'es'].contains(locale.languageCode);

@anular
Future<CustomLocalizations> load(Locale locale) {
regreso
SynchronousFuture<LocalizacionesPersonalizadas>(LocalizacionesPersonalizadas(locale));
}

@override
bool shouldReload(CustomLocalizationsDelegate old) => false;
}

• Aquí, CustomLocalizations se crea para admitir la localización de títulos y mensajes en la aplicación y


CustomLocalizationsDelegate se usa para cargar CustomLocalizations.

• Agregue delegados para MaterialApp, WidgetsApp y CustomLocalization usando las propiedades de


MaterialApp, localizationsDelegates y supportedLocales como se especifica a continuación:

localizationsDelegates: [ const
CustomLocalizationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
configuraciones regionales
admitidas: [ const Locale('en', ''),

141
Machine Translated by Google

Aleteo

const Locale('es', ''),


],

• Utilice el método CustomLocalizations para obtener el valor localizado del título y el mensaje
y utilícelo en el lugar apropiado como se especifica a continuación:

clase MyHomePage extiende StatelessWidget {


MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@anular
Compilación del widget (contexto BuildContext) {
andamio de vuelta(
barra de aplicaciones: barra de aplicaciones (

título: Texto (Localizaciones personalizadas


.de(contexto)
.título),
),
cuerpo: Centro(
niño: Columna (
mainAxisAlignment: MainAxisAlignment.centro,
niños: <Widget>[
Texto(
Localizaciones personalizadas
.de(contexto)
.mensaje,
),
],
),
),
);
}

• Aquí, hemos modificado la clase MyHomePage de StatefulWidget a StatelessWidget por razones de


simplicidad y usamos CustomLocalizations para obtener el título y el mensaje.

• Compilar y ejecutar la aplicación. La aplicación mostrará su contenido en inglés.

• Cerrar la aplicación. Vaya a Configuración -> Sistema -> Idiomas y entrada ->
Idiomas*

• Haz clic en Agregar una opción de idioma y selecciona Español. Esto instalará el idioma español y
luego lo listará como una de las opciones.

• Seleccione Español y muévalo arriba de Inglés. Esto establecerá como primer idioma el español y
todo se cambiará a texto en español.

• Ahora reinicie la aplicación de internacionalización y verá el título y


mensaje en idioma español.

• Podemos revertir el idioma a inglés moviendo la opción de inglés arriba de español


opción en la configuración.

• El resultado de la aplicación (en español) se muestra en la siguiente captura de pantalla:

142
Machine Translated by Google

Aleteo

Uso del paquete intl


Flutter proporciona un paquete internacional para simplificar aún más el desarrollo de aplicaciones móviles
localizadas. El paquete intl proporciona métodos y herramientas especiales para generar semiautomáticamente
mensajes específicos del idioma.

Vamos a crear una nueva aplicación localizada utilizando el paquete intl y entender el concepto.

• Crear una nueva aplicación flutter, flutter_intl_app

• Abra pubspec.yaml y agregue los detalles del paquete.

dependencias:
aleteo:
SDK: aleteo
flutter_localizaciones: sdk:
flutter
intl: ^0.15.7
intl_translation: ^0.17.3

143
Machine Translated by Google

Aleteo

• Android Studio mostrará la alerta como se muestra a continuación informando que pubspec.yaml
se actualiza

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo
configurará correctamente para la aplicación.

• Copie el main.dart de la muestra anterior, flutter_internationalization_app

• Importe el paquete internacional como se muestra a continuación:

importar 'paquete: intl/intl.dart';

• Actualice la clase CustomLocalization como se muestra en el código que figura a continuación:

class Localizaciones personalizadas {


static Future<CustomLocalizations> load(Locale locale) {
cadena final nombre = locale.countryCode.isEmpty ? locale.languageCode : locale.toString();
final String localeName = Intl.canonicalizedLocale(nombre);

devuelve initializeMessages(localeName).then((_) {
Intl.defaultLocale = localeName; return
CustomLocalizations(); });

Localizaciones personalizadas estáticas de (contexto BuildContext) {


return Localizations.of<CustomLocalizations>(context,
CustomLocalizations);
}

String get title { return


Intl.message( 'Demo',
name: 'title', desc: 'Title
for the Demo
application', );

Cadena obtener mensaje{


return Intl.message('Hello
World', name:
'message', desc:
'Message for the Demo application', );

}
}

clase CustomLocalizationsDelegate extiende


LocalizationsDelegate<CustomLocalizations> { const
CustomLocalizationsDelegate();

@anular

144
Machine Translated by Google

Aleteo

bool es compatible => ['en',


'es'].contains(locale.languageCode);

@anular
Future<CustomLocalizations> load(Locale locale) {
devuelve CustomLocalizations.load(locale);
}

@anular
bool shouldReload(CustomLocalizationsDelegate old) => false;
}

• Aquí, hemos usado tres métodos del paquete intl en lugar de métodos personalizados.
De lo contrario, los conceptos son los mismos.

• Intl.canonicalizedLocale: se utiliza para obtener el nombre de configuración regional correcto.

• Intl.defaultLocale: se utiliza para establecer la configuración regional actual.

• Intl.message: se utiliza para definir nuevos mensajes.

• importar el archivo l10n/messages_all.dart . Generaremos este archivo en breve.

importar 'l10n/messages_all.dart';

• Ahora, cree una carpeta, lib/l10n

• Abra un símbolo del sistema y vaya al directorio raíz de la aplicación (donde pubspec.yaml
está disponible) y ejecute el siguiente comando:

flutter paquetes pub ejecutar intl_translation:extract_to_arb --output dir=lib/l10n lib/


main.dart

• Aquí, el comando generará un archivo intl_message.arb, una plantilla para crear un mensaje en una
configuración regional diferente. El contenido del archivo es el siguiente:

{
"@@last_modified": "2019-04-19T02:04:09.627551",
"título": "Demostración",
"@título": {
"description": "Título de la aplicación de demostración",
"teclee el texto",
"marcadores de posición": {}
},
"mensaje": "Hola Mundo",
"@mensaje": {
"description": "Mensaje para la aplicación de demostración",
"teclee el texto",
"marcadores de posición": {}
}
}

• Copie intl_message.arb y cree un nuevo archivo, intl_en.arb

• Copie intl_message.arb y cree un nuevo archivo, intl_es.arb y cambie el contenido al idioma español
como se muestra a continuación:

145
Machine Translated by Google

Aleteo

{
"@@last_modified": "2019-04-19T02:04:09.627551", "title":
"Manifestación", "@title": { "description": "Título para la
aplicación Demo", "type": "text ", "marcadores de posición":
{} }, "mensaje": "Hola Mundo", "@mensaje": {

"descripción": "Mensaje para la aplicación de demostración",


"tipo": "texto", "marcadores de posición": {}

}
}

• Ahora, ejecute el siguiente comando para crear el archivo de mensaje final, message_all.dart

flutter paquetes pub run intl_translation:generate_from_arb --output dir=lib\l10n --no-use-


deferred-loading lib\main.dart lib\l10n\intl_en.arb lib\l10n\intl_es.arb

• Compilar y ejecutar la aplicación. Funcionará de manera similar a la aplicación anterior, flutter_localization_app.

146
Machine Translated by Google

17. Flutter – Pruebas Aleteo

La prueba es una fase muy importante en el ciclo de vida de desarrollo de una aplicación. Asegura que la
aplicación es de alta calidad. Las pruebas requieren una cuidadosa planificación y ejecución. También es la
fase del desarrollo que consume más tiempo.

El lenguaje Dart y el marco Flutter brindan un amplio soporte para la prueba automatizada de una aplicación.

Tipos de pruebas

Generalmente, hay tres tipos de procesos de prueba disponibles para probar completamente una aplicación.
Son los siguientes:

Examen de la unidad

La prueba unitaria es el método más fácil para probar una aplicación. Se basa en asegurar la corrección de
un fragmento de código (una función, en general) o un método de una clase. Pero, no refleja el entorno real
y, en consecuencia, es la opción de menor importancia para encontrar los errores.

Prueba de widgets

La prueba de widgets se basa en garantizar la corrección de la creación, el procesamiento y la interacción


con otros widgets como se esperaba. Va un paso más allá y proporciona un entorno casi en tiempo real para
encontrar más errores.

Pruebas de integración

Las pruebas de integración involucran tanto pruebas unitarias como pruebas de widgets junto con
componentes externos de la aplicación como base de datos, servicio web, etc. Simula o se burla del entorno
real para encontrar casi todos los errores, pero es el proceso más complicado.

Flutter brinda soporte para todo tipo de pruebas. Brinda soporte extenso y exclusivo para pruebas de widgets.
En este capítulo, discutiremos las pruebas de widgets en detalle.

Prueba de widgets

El marco de prueba de Flutter proporciona el método testWidgets para probar widgets. Acepta dos argumentos:

• Descripción de la prueba

• código de prueba

testWidgets('descripción de la prueba: encontrar un widget', '<código de prueba>');

147
Machine Translated by Google

Aleteo

Pasos involucrados
La prueba de widgets implica tres pasos distintos:

• Renderizar el widget en el entorno de prueba.

• WidgetTester es la clase proporcionada por el marco de prueba de Flutter para construir y renderizar el
widget. El método pumpWidget de la clase WidgetTester acepta cualquier widget y lo representa en el
entorno de prueba.

testWidgets('busca una instancia específica', (WidgetTester tester) async {


esperar probador.pumpWidget(MaterialApp(
casa: Andamio(
cuerpo: Texto('Hola'),
),
));
});

• Encontrar el widget, que necesitamos probar.

• Flutter framework proporciona muchas opciones para encontrar los widgets representados en el entorno
de prueba y generalmente se denominan Finders. Los buscadores más utilizados son find.text,
find.byKey y find.byWidget
• find.text encuentra el widget que contiene el texto especificado.

find.text('Hola')

• find.byKey encuentra el widget por su clave específica.

encontrar.byKey('inicio')

• find.byWidget encuentra el widget por su variable de instancia

find.byWidget(homeWidget)

• Asegurarse de que el widget funcione como se esperaba.

• Flutter framework proporciona muchas opciones para hacer coincidir el widget con el widget esperado
y normalmente se denominan Matchers. Podemos usar el método esperado proporcionado por el marco
de prueba para hacer coincidir el widget, que encontramos en el segundo paso con nuestro widget
esperado al elegir cualquiera de los emparejadores. Algunos de los emparejadores importantes son los
siguientes:

• findsOneWidget: verifica que se encuentra un solo widget.

expect(find.text('Hola'), encuentraUnWidget);

• findsNothing: verifica que no se encuentran widgets.

expect(find.text('Hola Mundo'), no encuentraNada);

• findsWidgets: verifica que se encuentra más de un solo widget.

148
Machine Translated by Google

Aleteo

expect(find.text('Guardar'), encuentraWidgets);

• findsNWidgets: verifica que se encuentran N número de widgets.

expect(find.text('Guardar'), findsNWidgets(2));

El código de prueba completo es el siguiente:

testWidgets('busca el widget hello', (WidgetTester tester) async { await


tester.pumpWidget(MaterialApp( home: Scaffold( body: Text('Hello'),

),
));

expect(find.text('Hola'), encuentraUnWidget);
});

Aquí, representamos un widget de MaterialApp con el texto Hola usando el widget de texto en su cuerpo.
Luego, usamos find.text para encontrar el widget y luego lo emparejamos usando findsOneWidget.

Ejemplo de trabajo
Vamos a crear una aplicación de aleteo simple y escribir una prueba de widget para comprender mejor los
pasos involucrados y el concepto.

• Cree una nueva aplicación flutter, flutter_test_app en Android Studio.

• Abra widget_test.dart en la carpeta de prueba. Tiene un código de prueba de muestra como se indica a continuación:

testWidgets('Contador incrementa la prueba de humo', (WidgetTester tester) async {


// Crea nuestra aplicación y activa un marco.
espera tester.pumpWidget(MyApp());

// Verifica que nuestro contador comience en 0.


expect(find.text('0'), findsOneWidget);
expect(find.text('1'), no encuentraNada);

// Toque el ícono '+' y active un cuadro. await


tester.tap(find.byIcon(Icons.add)); esperar
probador.bomba();

// Verificamos que nuestro contador haya incrementado.


esperar(buscar.texto('0'), no encuentraNada);
expect(find.text('1'), encuentraUnWidget); });

• Aquí, el código de prueba tiene las siguientes funcionalidades:

• Renderiza el widget MyApp usando tester.pumpWidget

• Se asegura de que el contador sea inicialmente cero usando findsOneWidget y findsNothing


emparejadores

149
Machine Translated by Google

Aleteo

• Encuentra el botón de incremento del contador usando el método find.byIcon.

• Toca el botón de incremento del contador usando el método tester.tap.

• Asegura que el contador se incremente usando findsOneWidget y findsNothing


emparejadores

• Toquemos nuevamente el botón de incremento del contador y luego verifiquemos si el contador aumenta
a dos.

await tester.tap(find.byIcon(Icons.add));
esperar probador.bomba();

expect(find.text('2'), encuentraUnWidget);

• Haga clic en el menú Ejecutar.

• Haga clic en pruebas en la opción widget_test.dart. Esto ejecutará la prueba e informará el resultado en
la ventana de resultados.

150
Machine Translated by Google

18. Flutter – Despliegue Aleteo

Este capítulo explica cómo implementar la aplicación Flutter en las plataformas Android e iOS.

Aplicación Android
• Cambie el nombre de la aplicación usando la entrada android:label en el archivo de manifiesto de Android.
El archivo de manifiesto de la aplicación Android, AndroidManifest.xml se encuentra en <directorio de la aplicación>/
android/app/src/main. Contiene detalles completos sobre una aplicación de Android.
Podemos configurar el nombre de la aplicación usando la entrada android:label.

• Cambie el ícono del lanzador usando la entrada android:icon en el archivo de manifiesto.

• Firme la aplicación usando la opción estándar según sea necesario

• Habilite Proguard y Ofuscación usando la opción estándar, si es necesario.

• Cree un archivo APK de lanzamiento ejecutando el siguiente comando:

cd /ruta/a/mi/aplicación
aleteo construir apk

• Puede ver una salida como se muestra a continuación:

Inicializando gradle... 8.6s


Resolviendo dependencias... 19.9s
Llamar a la transformación del artefacto JAR simulado para crear el archivo: /
Users/.gradle/caches/transforms-1/files-1.1/android.jar/
c30932f130afbf3fd90c131ef9069a0b/android.jar con entrada /Users/Library/
Android/sdk/platforms/android-28/android.jar
Ejecutando la tarea de Gradle 'assembleRelease'...
Ejecutando la tarea de Gradle 'assembleRelease'... Listo 85.7 s
Construido build/app/outputs/apk/release/app-release.apk (4.8MB).

• Instale el APK en un dispositivo usando el siguiente comando:

instalación de aleteo

• Publique la aplicación en Google Playstore creando un paquete de aplicaciones e introdúzcalo en la tienda de


juegos usando métodos estándar.

paquete de aplicaciones de compilación flutter

Aplicación iOS
• Registre la aplicación iOS en App Store Connect utilizando el método estándar. Guarde el ID de paquete utilizado
al registrar la aplicación.

• Actualice el nombre para mostrar en la configuración del proyecto XCode para establecer el nombre de la aplicación.

151
Machine Translated by Google

Aleteo

• Actualice el identificador del paquete en la configuración del proyecto XCode para establecer la identificación del paquete, que
utilizado en el paso 1.

• Señal de código según sea necesario utilizando el método estándar.

• Agregue un nuevo ícono de aplicación según sea necesario utilizando el método estándar.

• Generar archivo IPA usando el siguiente comando:

aleteo construir ios

• Ahora, puede ver el siguiente resultado:

Construyendo com.example.MyApp para dispositivo (ios-release)...


Firma automática de iOS para la implementación de dispositivos mediante el equipo de desarrollo especificado
en el proyecto Xcode: ejecución de la compilación Xcode...
23,5 s
......................

• Pruebe la aplicación insertando el archivo IPA de la aplicación en TestFlight utilizando el método


estándar.

• Finalmente, inserte la aplicación en la App Store utilizando el método estándar.

152
Machine Translated by Google

Aleteo
19. Flutter – Herramientas de desarrollo

Este capítulo explica en detalle las herramientas de desarrollo de Flutter. La primera versión estable del kit de herramientas
de desarrollo multiplataforma se lanzó el 4 de diciembre de 2018, Flutter 1.0.
Bueno, Google está trabajando continuamente en las mejoras y fortaleciendo el marco Flutter con diferentes herramientas
de desarrollo.

Conjuntos de widgets

Google actualizó los conjuntos de widgets de Material y Cupertino para proporcionar una calidad de píxeles perfecta en el
diseño de los componentes. La próxima versión de flutter 1.2 se diseñará para admitir eventos de teclado de escritorio y
compatibilidad con el desplazamiento del mouse.

Desarrollo de Flutter con Visual Studio Code

Visual Studio Code es compatible con el desarrollo flutter y proporciona accesos directos extensos para un desarrollo rápido
y eficiente. Algunas de las funciones clave proporcionadas por Visual Studio Code para el desarrollo de flutter se enumeran
a continuación:

• Asistencia de código: cuando desee comprobar las opciones, puede utilizar Ctrl+Espacio para obtener una
lista de opciones de finalización de código.

• Solución rápida - Ctrl+. es una herramienta de solución rápida para ayudar a corregir el código.

• Accesos directos durante la codificación

• Proporciona documentación detallada en los comentarios.

• Atajos de depuración.

• Reinicios en caliente

Dart DevHerramientas

Podemos usar Android Studio o Visual Studio Code, o cualquier otro IDE para escribir nuestro código e instalar
complementos. El equipo de desarrollo de Google ha estado trabajando en otra herramienta de desarrollo llamada Dart
DevTools. Es una suite de programación basada en web. Es compatible con las plataformas Android e iOS. Se basa en la
vista de línea de tiempo para que los desarrolladores puedan analizar fácilmente sus aplicaciones.

Instalar herramientas de desarrollo

Para instalar DevTools, ejecute el siguiente comando en su consola:

flutter paquetes pub global activar devtools

Ahora puede ver el siguiente resultado:

Resolviendo dependencias...
+ argumentos 1.5.1
+ asíncrono 2.2.0

153
Machine Translated by Google

Aleteo

+ código char 1.1.2 +


código espejo 0.5.3+5.44.0
+ colección 1.14.11
+ convertir 2.1.1 +
herramientas de desarrollo 0.0.16
+ devtools_server 0.0.2 + http
0.12.0+2 + http_parser 3.1.3
+ intl 0.15.8

+ js 0.6.1 + 1 +
meta 1.1.7
+ mimo 0.9.6+2
..................
..................

Devtools ejecutables instalados.

Herramientas de desarrollo activadas 0.0.16.

Ejecutar servidor

Puede ejecutar el servidor DevTools con el siguiente comando:

flutter paquetes pub global run devtools

Ahora, obtendrá una respuesta similar a esta,

Sirviendo DevTools en http://127.0.0.1:9100

Comience su aplicación

Vaya a su aplicación, abra el simulador y ejecútelo usando el siguiente comando:

ejecución de aleteo --observatory-port=9200

Ahora, está conectado a DevTools.

Inicie DevTools en el navegador

Ahora acceda a la siguiente URL en el navegador para iniciar DevTools:

http://localhost:9100/?port=9200

Obtendrá una respuesta como se muestra a continuación:

154
Machine Translated by Google

Aleteo

SDK de aleteo
Para actualizar Flutter SDK, use el siguiente comando:

actualización de aleteo

Puede ver una salida como se muestra a continuación:

Para actualizar los paquetes de Flutter, use el siguiente comando:

actualización de paquetes flutter

Podrías ver la siguiente respuesta,

Ejecutando "actualización de paquetes flutter" en my_app... 7.4s

Inspector de aleteo
Se utiliza para explorar árboles de widgets de aleteo. Para lograr esto, ejecute el siguiente comando en
su consola,

155
Machine Translated by Google

Aleteo

flutter run --track-widget-creación

Puede ver una salida como se muestra a continuación:

Iniciando lib/main.dart en iPhone X en modo de depuración...

ÿMontando recursos de Flutter... 3,6 s


Compilando, enlazando y firmando... 6,8 s
Construcción de Xcode 14,2 s
hecha. 2904 ms (!)

Para recargar en caliente los cambios mientras se ejecuta, presione "r". Para reiniciar en caliente (y reconstruir el estado),
presione "R".
Un depurador y perfilador de Observatory en iPhone X está disponible en: http://127.0.0.1:50399/

Para un mensaje de ayuda más detallado, presione "h". Para separar, presione "d"; para salir, presione "q".

Ahora vaya a la url, http://127.0.0.1:50399/, podría ver el siguiente resultado:

156
Machine Translated by Google

Aleteo
20. Flutter: escritura de aplicaciones avanzadas

En este capítulo, vamos a aprender cómo escribir una aplicación móvil completa, cost_calculator. El propósito de
la calculadora_gastos es almacenar nuestra información de gastos. La característica completa de la aplicación es
la siguiente:

• Lista de gastos

• Formulario para ingresar nuevos gastos

• Opción para editar/eliminar los gastos existentes


• Gastos totales en cualquier instancia.

Vamos a programar la aplicación de calculadora de gastos utilizando las funciones avanzadas mencionadas a
continuación del marco Flutter.

• Uso avanzado de ListView para mostrar la lista de gastos

• Programación de formularios

• Programación de base de datos SQLite para almacenar nuestros

gastos • Manejo de estado scoped_model para simplificar nuestra programación.

Comencemos a programar la aplicación gasto_calculador .

• Cree una nueva aplicación Flutter, cost_calculator en Android Studio.

• Abra pubspec.yaml y agregue dependencias de paquetes.

dependencias:
aleteo:
SDK: aleteo

sqflite: ^1.1.0
path_provider: ^0.5.0+1
scoped_model: ^1.0.1 intl:
cualquiera

• Observe estos puntos aquí:

• sqflite se utiliza para la programación de bases de datos SQLite.

• path_provider se utiliza para obtener la ruta de la aplicación específica del sistema.

• scoped_model se utiliza para la gestión del estado.

• intl se utiliza para el formato de fecha

• Android Studio mostrará la siguiente alerta de que pubspec.yaml está actualizado.

• Haga clic en la opción Obtener dependencias. Android Studio obtendrá el paquete de Internet y lo configurará
correctamente para la aplicación.

157
Machine Translated by Google

Aleteo

• Eliminar el código existente en main.dart

• Agregue un archivo nuevo, Expense.dart para crear una clase de gastos. La clase de gastos tendrá las
siguientes propiedades y métodos.

• propiedad: id: identificación única para representar una entrada de gastos en la base de datos SQLite.

• propiedad: cantidad - Cantidad gastada.

• property: date - Fecha en que se gasta el importe.

• propiedad: categoría: la categoría representa el área en la que se gasta la cantidad.

por ejemplo, comida, viajes, etc.,

• formattedDate : se usa para dar formato a la propiedad de fecha

• fromMap : se utiliza para asignar el campo de la tabla de la base de datos a la propiedad en el

objeto de gasto y para crear un nuevo objeto de gasto

gastos de fábrica.fromMap(Map<String, dynamic> data) { return


Expense(datos['id'], datos['cantidad'], DateTime.parse(datos['fecha']),
datos['categoría']

);
}

• toMap : se usa para convertir el objeto de gastos en Dart Map, que se puede usar más en la
programación de bases de datos

Map<String, dinámico> toMap() => {


"id" : id,
"cantidad" : cantidad,
"fecha" : fecha.toString(),
"categoría" : categoría, };

• columnas : variable estática utilizada para representar el campo de la base de datos.

• Ingrese y guarde el siguiente código en el archivo Expense.dart.

importar 'paquete: intl/intl.dart';

gasto de clase { id
int final; cantidad
doble final; fecha final de
fecha y hora; categoría de
cadena final;

String get formattedDate { var


formatter = new DateFormat('yyyy-MM-dd'); volver
formateador.formato(esta.fecha);
}

columnas finales estáticas = ['id', 'cantidad', 'fecha', 'categoría'];

158
Machine Translated by Google

Aleteo

Expense(this.id, this.amount, this.date, this.category);

Gastos de fábrica. fromMap(Map<String, dynamic> data) { return


Expense(datos['id'], datos['cantidad'],

DateTime.parse(datos['fecha']),
datos['categoría'] );

Map<String, dinámico> toMap() => {


"id" : id,
"cantidad" : cantidad,
"fecha" : fecha.toString(),
"categoría" : categoría, };

• El código anterior es simple y se explica por sí mismo.

• Agregue un nuevo archivo, Database.dart para crear la clase SQLiteDbProvider. El propósito de la clase
SQLiteDbProvider es el siguiente:

• Obtenga todos los gastos disponibles en la base de datos utilizando el método getAllExpenses. Eso
se utilizará para enumerar toda la información de gastos del usuario.

Future<List<Expense>> getAllExpenses() asíncrono {


db final = base de datos en espera;

Resultados de List<Map> = await db.query("Gastos", columnas:


Gastos.columnas, orderBy: "fecha DESC");

List<Gastos> gastos = new List();


resultados.forEach((resultado) { Gasto gasto =
Gasto.fromMap(resultado); gastos.añadir(gasto); });

gastos de devolución;
}

• Obtenga información de gastos específica basada en la identidad de gastos disponible en la base de


datos mediante el método getExpenseById. Se utilizará para mostrar la información de gastos
particulares al usuario.

Future<Gastos> getExpenseById(int id) asíncrono {


db final = base de datos en espera;

var result = await db.query("Gastos", donde: "id = ", whereArgs: [id]);

159
Machine Translated by Google

Aleteo

devolver resultado.isNotEmpty ? Expense.fromMap(resultado.primero) : Nulo;


}

• Obtenga los gastos totales del usuario utilizando el método getTotalExpense. Será
se utiliza para mostrar el gasto total actual al usuario.

Future<doble> getTotalExpense() asíncrono {


db final = base de datos en espera;

List<Map> list = await db.rawQuery("Seleccione SUM(cantidad) como cantidad


del gasto");

volver list.isNotEmpty ? list[0]["cantidad"] : Nulo;


}

• Agregar nueva información de gastos a la base de datos utilizando el método de inserción. Va a


ser utilizado para agregar una nueva entrada de gastos en la aplicación por parte del usuario.

Futuro <Gasto> insertar (Gasto de gasto) asíncrono {


db final = base de datos en espera;

var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id


FROM Expense");
var id = maxIdResult.first["last_inserted_id"];

var resultado = esperar db.rawInsert(


"INSERTAR en gastos (id, monto, fecha, categoría)"
"
VALORES (?, ?, ?, ?)",
[id, gasto.cantidad, gasto.fecha.toString(), gasto.categoría]

);

return Gasto(id, gasto.cantidad, gasto.fecha, gasto.categoría);

• Actualice la información de gastos existente utilizando el método de actualización. Se utilizará para


editar y actualizar la entrada de gastos existente disponible en el sistema por parte del usuario.

actualización (producto de gastos) asíncrono {


db final = base de datos en espera;

var resultado = esperar db.update("Gasto", producto.toMap(),


donde: "id = ?", whereArgs: [producto.id]);

resultado devuelto;
}

• Eliminar la información de gastos existente utilizando el método de eliminación. Se utilizará para


eliminar la entrada de gastos existente disponible en el sistema por parte del usuario.

eliminar (int id) asíncrono {


db final = base de datos en espera;

160
Machine Translated by Google

Aleteo

db.delete("Gastos", donde: "id = ?", whereArgs: [id]);


}

• El código completo de la clase SQLiteDbProvider es el siguiente:

importar 'dardo: asíncrono';


importar 'dardo:io'; import
'paquete:ruta/ruta.dart';

importar 'paquete: ruta_proveedor/ruta_proveedor.dart'; importar


'paquete: sqflite/sqflite.dart';

importar 'Gastos.dart';

clase SQLiteDbProvider
{ SQLiteDbProvider._();

SQLiteDbProvider final estático db = SQLiteDbProvider._();

Base de datos estática _base de datos;

Future<Database> get base de datos asíncrona


{ if (_database != null) return _database;

_base de datos = espera initDB();


devolver _base de datos;
}

initDB() asíncrono {
Directorio documentosDirectorio = espera getApplicationDocumentsDirectory(); String path =
join(documentsDirectory.path, "ExpenseDB2.db"); return await openDatabase (ruta, versión:
1, onOpen: (db) {}, onCreate: (Base de datos db, versión int) asíncrono {

await db.execute("CREATE TABLE Expense (" "ID


INTEGER PRIMARY KEY", "cantidad REAL",
"TEXTO de fecha", "TEXTO de categoría" ")");

await
db.execute( "INSERTAR EN Gastos ('id', 'cantidad', 'fecha', 'categoría') valores
(?, ?, ?, ?)", [1, 1000, '2019-04-01 10 :00:00', "Comida"]);

/*esperar
db.execute( "INSERTAR EN EL Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[2, "Pixel", "Pixel es el teléfono con más funciones de la historia", 800,

161
Machine Translated by Google

Aleteo

"píxel.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[3, "Laptop", "La computadora portátil es la herramienta de desarrollo más productiva",
2000, "laptop.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[4, "Tablet", "La computadora portátil es la herramienta de desarrollo más productiva",
1500, "tablet.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[5, "Pendrive", "iPhone es el teléfono con estilo de todos los tiempos", 100,
"pendrive.png"]);

esperar db.ejecutar(
"INSERTAR EN Producto ('id', 'nombre', 'descripción', 'precio',
'imagen') valores (?, ?, ?, ?, ?)",
[6, "Floppy Drive", "iPhone es el teléfono con más estilo", 20, "floppy.png"]);

*/
});
}

Future<List<Expense>> getAllExpenses() asíncrono {


db final = base de datos en espera;

Resultados de List<Map> = await db.query("Gastos", columnas: Gastos.columnas,


orderBy: "fecha DESC");

List<Gastos> gastos = new List();


resultados.forEach((resultado) {
Gasto gasto = Gasto.fromMap(resultado);
gastos.add(gastos);
});

gastos de devolución;
}

Future<Gastos> getExpenseById(int id) asíncrono {


db final = base de datos en espera;

var result = await db.query("Gastos", donde: "id = ", whereArgs: [id]);

devolver resultado.isNotEmpty ? Expense.fromMap(resultado.primero) : Null;


}

Future<doble> getTotalExpense() asíncrono {


db final = base de datos en espera;

162
Machine Translated by Google

Aleteo

List<Map> list = await db.rawQuery("Seleccione SUM(cantidad) como cantidad de gastos");

volver list.isNotEmpty ? list[0]["cantidad"] : Nulo;


}

Futuro <Gasto> insertar (Gasto de gasto) asíncrono {


db final = base de datos en espera;

var maxIdResult = await db.rawQuery("SELECT MAX(id)+1 as last_inserted_id FROM Expense");

var id = maxIdResult.first["last_inserted_id"];

var resultado = esperar db.rawInsert(


"INSERTAR en gastos (id, monto, fecha, categoría)"
"
VALORES (?, ?, ?, ?)",
[id, gasto.cantidad, gasto.fecha.toString(), gasto.categoría]
);

return Gasto(id, gasto.cantidad, gasto.fecha, gasto.categoría);


}

actualización (producto de gastos) asíncrono {


db final = base de datos en espera;

var resultado = esperar db.update("Gasto", producto.toMap(),


donde: "id = ?", whereArgs: [producto.id]);

resultado devuelto;
}

eliminar (int id) asíncrono {


db final = base de datos en espera;

db.delete("Gastos", donde: "id = ?", whereArgs: [id]);


}
}

• Aquí,

• base de datos es la propiedad para obtener el objeto SQLiteDbProvider.


• initDB es un método utilizado para seleccionar y abrir la base de datos SQLite.

• Cree un archivo nuevo, ExpenseListModel.dart para crear ExpenseListModel. El propósito del modelo es
mantener la información completa de los gastos del usuario en la memoria y actualizar la interfaz de usuario
de la aplicación cada vez que cambia el gasto del usuario en la memoria. Se basa en la clase Model del
paquete scoped_model. Tiene las siguientes propiedades y métodos:

• _items - lista privada de gastos

• items - getter para _items como UnmodifiableListView<Expense> para evitar cambios


inesperados o accidentales en la lista.

• totalExpense: captador de gastos totales en función de la variable de artículos.

163
Machine Translated by Google

Aleteo

double get gasto total { cantidad


doble = 0.0; for(var i = 0; i <
_items.length; i++) { cantidad = cantidad +
_items[i].cantidad;
}

importe devuelto;
}

• load: se utiliza para cargar los gastos completos de la base de datos y en la variable _items.
También llama a notificarListeners para actualizar la interfaz de usuario.

void load()
{ Future<List<Expense>> list = SQLiteDbProvider.db.getAllExpenses();

list.then( (dbItems) { for(var i =


0; i < dbItems.length; i++) {
_items.add(dbItems[i]); }

notificar a los oyentes


(); });
}

• byId: se utiliza para obtener un gasto particular de la variable _items.

Gasto byId(int id) { for(var i =


0; i < _items.length; i++) { if(_items[i].id == id) { return
_items[i];

}
}

devolver nulo;
}

• agregar: se usa para agregar un nuevo elemento de gasto en la variable _items, así como en la base
de datos. También llama a notificarListeners para actualizar la interfaz de usuario.

void add(artículo de gastos)


{ SQLiteDbProvider.db.insert(artículo).then((val) {
_elementos.añadir(valor);

notificar a los oyentes


(); });
}

• agregar: se usa para agregar un nuevo elemento de gasto en la variable _items, así como en la base de
datos. También llama a notificarListeners para actualizar la interfaz de usuario.

actualización nula (partida de gastos) {

booleano encontrado = falso;

164
Machine Translated by Google

Aleteo

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { _items[i] = item; encontrado = verdadero;
SQLiteDbProvider.db.update(elemento); descanso;

}
}

si (encontrado) notificar a los oyentes ();


}

• eliminar: se utiliza para eliminar un elemento de gasto existente en la variable _items, así como
de la base de datos También llama a notificarListeners para actualizar la interfaz de usuario.

anular eliminar (elemento de gasto) {

booleano encontrado = falso;

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { encontrado = verdadero;
SQLiteDbProvider.db.delete(elemento.id);
_elementos.removeAt(i); descanso;

}
}

si (encontrado) notificar a los oyentes ();


}

• El código completo de la clase ExpenseListModel es el siguiente:

importar 'dardo: colección'; import


'paquete:scoped_model/scoped_model.dart'; importar
'Gastos.dart'; importar 'Base de datos.dart';

class ExpenseListModel extiende el modelo {

ExpenseListModel() { this.load(); }

lista final<gastos> _items = [];

UnmodifiableListView<Expense> obtener elementos =>


Vista de lista no modificable (_ elementos);

/*Future<double> get totalExpense { return


SQLiteDbProvider.db.getTotalExpense(); }*/

double get gasto total { cantidad


doble = 0.0;

165
Machine Translated by Google

Aleteo

for(var i = 0; i < _items.length; i++) { cantidad = cantidad


+ _items[i].cantidad;
}

importe devuelto;
}

void load()
{ Future<List<Expense>> list =
SQLiteDbProvider.db.getAllExpenses();

list.then( (dbItems) { for(var i =


0; i < dbItems.length; i++) {
_items.add(dbItems[i]);
}

notificar a los oyentes


(); });
}

Gasto byId(int id) { for(var i =


0; i < _items.length; i++) { if(_items[i].id == id) { return
_items[i];

}
}

devolver nulo;
}

void add(artículo de gastos)


{ SQLiteDbProvider.db.insert(artículo).then((val) {
_elementos.añadir(valor);

notificar a los oyentes


(); });
}

actualización nula (partida de gastos) {

booleano encontrado = falso;

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { _items[i] = item; encontrado = verdadero;
SQLiteDbProvider.db.update(elemento); descanso;

}
}

si (encontrado) notificar a los oyentes ();


}

anular eliminar (elemento de gasto) {

166
Machine Translated by Google

Aleteo

booleano encontrado = falso;

for(var i = 0; i < _items.length; i++) { if(_items[i].id ==


item.id) { encontrado = verdadero;
SQLiteDbProvider.db.delete(elemento.id);
_elementos.removeAt(i); descanso;

}
}

si (encontrado) notificar a los oyentes ();


}
}

• Abra el archivo main.dart. Importe las clases como se especifica a continuación:

importar 'paquete: flutter/material.dart'; import


'paquete:scoped_model/scoped_model.dart'; import
'ExpenseListModel.dart';

importar 'Gastos.dart';

• Agregue la función principal y llame a runApp pasando ScopedModel<ExpenseListModel>


artilugio.

void main()
{ gastos finales = ExpenseListModel();

runApp(ScopedModel<ExpenseListModel>( modelo:
gastos, niño: MiAplicación(), ));

• Aquí,

• objeto de gastos carga toda la información de gastos del usuario de la base de datos.
Además, cuando la aplicación se abre por primera vez, creará la base de datos
necesaria con las tablas adecuadas.

• ScopedModel proporciona la información de gastos durante todo el ciclo de vida de la aplicación


y asegura el mantenimiento del estado de la aplicación en cualquier instancia. Nos permite
usar StatelessWidget en lugar de StatefulWidget.

• Cree una MyApp simple usando el widget MaterialApp.

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Gasto', tema:
ThemeData(

167
Machine Translated by Google

Aleteo

PrimarySwatch: Colors.blue, ), home:


MyHomePage(title: 'Calculadora de
gastos'), );

}
}

• Cree el widget MyHomePage para mostrar toda la información de gastos del usuario junto con los gastos
totales en la parte superior. El botón flotante en la esquina inferior derecha se usará para agregar nuevos
gastos.

clase MyHomePage extiende StatelessWidget


{ MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title: Text(this.title), ),
body:

ScopedModelDescendant<ExpenseListModel>( builder:
(contexto, hijo, gastos) { return ListView.separated(

itemCount: gastos.items == null ? 1:


gastos.artículos.longitud + 1,
itemBuilder: (contexto, índice) { if (índice
== 0) { return ListTile(título:
Texto("Gastos totales:
" +
gastos.totalExpense.toString(), estilo:
TextStyle(fontSize: 24,fontWeight: FontWeight.bold), )

); } más
{ índice = índice - 1;
return Desechable( clave:
Clave(gastos.elementos[índice].id.toString()), onDesechado:
(dirección) {
gastos.eliminar(gastos.artículos[índice]);

Scaffold.of(contexto).showSnackBar(SnackBar( contenido:
+
Texto("Artículo con id, " gastos.artículos[índice].id.toString()

+
"se despide")));
},
child: ListTile( onTap:
()

{ Navigator.push( context,
MaterialPageRoute( builder: (context) =>
FormPage( id:
gastos.artículos[índice].id,

168
Machine Translated by Google

Aleteo

gastos: gastos, )));

},
inicial: icono (Icons.monetization_on), final: Icon
(Icons.keyboard_arrow_right),

título: Texto(gastos.artículos[índice].categoría
+
": " +

gastos.elementos[índice].cantidad.toString() + " \ngastado


" +
en
gastos.elementos[índice].formattedDate, estilo:
TextStyle(fontSize: 18, fontStyle: FontStyle.italic),))); } }, separatorBuilder: (contexto, índice)
{ return Divider(); }, ); }, ), botón de acción flotante:
ScopedModelDescendant<Modelo de lista de gastos> (constructor:
(contexto, hijo, gastos) { return Botón de acción flotante (onPressed: ()
{

Navigator.push( contexto,
MaterialPageRoute( constructor:
(contexto) => ScopedModelDescendant<ExpenseListModel>(
constructor: (contexto, hijo, gastos) { return
FormPage( id: 0,

gastos: gastos, ); }))); //


gastos.add(nuevo Gasto( 2,
1000, DateTime.parse('2019-04-01
11:00:00'), //

'Comida'));
// imprimir(gastos.artículos.longitud); },
información sobre herramientas: 'Incremento',
hijo: Icon(Icons.add), ); }));

}
}

• Aquí,

• ScopedModelDescendant se usa para pasar el modelo de gastos a ListView y


Widget de botón de acción flotante.

169
Machine Translated by Google

Aleteo

• El widget ListView.separated y ListTile se usa para enumerar la información de gastos.

• El widget descartable se usa para eliminar la entrada de gastos con un gesto de deslizar.

• Navegador se utiliza para abrir la interfaz de edición de una entrada de gastos. Puede ser activado por
tocando una entrada de gastos.

• Crear un widget FormPage. El propósito del widget FormPage es agregar o actualizar una entrada de
gastos. También maneja la validación de la entrada de gastos.

clase FormPage extiende StatefulWidget {


FormPage({Clave clave, este.id, este.gastos}) : super(clave: clave);

identificación
interna final; gastos finales de ExpenseListModel;

@anular
_FormPageState createState() => _FormPageState(id: id, gastos: gastos); }

clase _FormPageState extiende Estado<FormPage> {


_FormPageState({Clave clave, this.id, this.expenses});

identificación
interna final; gastos finales de ExpenseListModel;

scaffoldKey final = GlobalKey<ScaffoldState>(); formKey final


= GlobalKey<FormState>();

doble _cantidad;
FechaHora _fecha;
Cadena _categoría;

vacío _enviar() {
formulario final = formKey.currentState;

if (formulario.validar())
{ formulario.guardar();

si (este.id == 0)
gastos.add(Gasto(0, _cantidad, _fecha, _categoría)); else
gastos.update(Expense(this.id, _amount, _date, _category));

Navigator.pop(contexto);
}
}

@anular
Creación de widgets (contexto BuildContext)
{ return Scaffold (
clave: scaffoldKey,
appBar: AppBar(título:
Texto('Ingresar detalles de gastos'),

170
Machine Translated by Google

Aleteo

),
cuerpo: Relleno
(relleno: const EdgeInsets.all (16.0), niño:
Formulario (clave: formKey, niño: Columna
(niños: [TextFormField (estilo: TextStyle
(fontSize: 22), decoración: const
InputDecoration (icono: const
Icon(Icons.monetization_on), labelText:
'Cantidad', labelStyle: TextStyle(fontSize:

18)),
validador: (val) {
Patrón patrón = r'^[1-9]\d*(\.\d+)?$'; RegExp
expresión regular = new RegExp(patrón); if (!
regex.hasMatch(val)) devuelve 'Ingrese un número
válido'; demás

devolver nulo;
},
valor inicial:
identificación == 0 ? '' : gastos.byId(id).amount.toString(), onSaved: (val)

=> _amount = double.parse(val), ), TextFormField( estilo:


TextStyle(fontSize: 22), decoración: const InputDecoration(

icon: const Icon(Icons.calendar_today), hintText:


'Ingrese la fecha', labelText: 'Date', labelStyle:
TextStyle(fontSize: 18), validator: (val) {

Patrón patrón =
r'^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1- 9]| [12][0-9]|3[01])$';

RegExp expresión regular = new


RegExp(patrón); si (!regex.hasMatch(val))
return 'Ingrese una fecha válida'; de lo
contrario, devuelve nulo;

},
onSaved: (val) => _date = DateTime.parse(val), initialValue:
id == 0 ? '' gastos.byId(id).formattedDate,
: keyboardType:
TextInputType.datetime, ), TextFormField( estilo: TextStyle(fontSize: 22),
decoración: const InputDecoration( icon: const
Icon(Icons.category), labelText:

'Categoría', labelStyle: TextStyle(fontSize: 18)), onSaved: (val)


=> _category = val, initialValue: id == 0 ? ''

: gastos.byId(id).category.toString(),

171
Machine Translated by Google

Aleteo

),

RaisedButton( onPressed:
_submit, child: new
Text('Submit'), ), ], ), ), ), );

}
}

• Aquí,

• TextFormField se utiliza para crear una entrada de formulario.

• La propiedad validator de TextFormField se usa para validar el elemento de formulario junto con
Patrones RegEx.

• La función _submit se usa junto con el objeto de gastos para agregar o actualizar los gastos en la
base de datos.

• El código completo del archivo main.dart es el siguiente:

importar 'paquete: flutter/material.dart'; import


'paquete:scoped_model/scoped_model.dart'; import
'ExpenseListModel.dart';

importar 'Gastos.dart';

vacío principal() {
gastos finales = ExpenseListModel();

runApp(ScopedModel<ExpenseListModel>( modelo:
gastos, niño: MiAplicación(), ));

class MyApp extiende StatelessWidget { // Este


widget es la raíz de su aplicación. @override Widget
compilación (contexto BuildContext) {

return MaterialApp(título:
'Gastos', tema:

ThemeData(primarioSwatch:
Colors.blue, ), home:
MyHomePage(título: 'Calculadora de gastos'), );

}
}

clase MyHomePage extiende StatelessWidget {

172
Machine Translated by Google

Aleteo

MyHomePage({Key key, this.title}) : super(key: key);

título final de la cadena;

@override
Widget build(BuildContext context) { return
Scaffold( appBar: AppBar( title: Text(this.title), ),
body:

ScopedModelDescendant<ExpenseListModel>( builder:
(contexto, hijo, gastos) { return ListView.separated(

itemCount: gastos.items == null ? 1:


gastos.artículos.longitud + 1,
itemBuilder: (contexto, índice) { if (índice
== 0) { return ListTile(título:
Texto("Gastos totales:
" +
gastos.totalExpense.toString(), estilo:
TextStyle(fontSize: 24,fontWeight: FontWeight.bold), )

); } más
{ índice = índice - 1;
return Desechable( clave:
Clave(gastos.elementos[índice].id.toString()), onDesechado:
(dirección) {
gastos.eliminar(gastos.artículos[índice]);

Scaffold.of(context).showSnackBar(SnackBar( content:
Text("Elemento con id, " +
gastos.elementos[índice].id.toString() + " se
descarta")));
},
child: ListTile( onTap:
()

{ Navigator.push( context,
MaterialPageRoute( builder: (context) => FormPage(
id: gastos.artículos[índice].id,
gastos: gastos, )));

},
inicial: Icono (Icons.monetization_on), final: Icon
(Icons.keyboard_arrow_right), título: Texto (gastos.elementos
[índice]. categoría +
": " +
gastos.artículos[índice].cantidad.toString() + " \ngastado
" +
en gastos.artículos[índice].formattedDate, estilo:

TextStyle(fontSize: 18, fontStyle: FontStyle.cursiva),)));

} },
separadorBuilder: (contexto, índice) {

173
Machine Translated by Google

Aleteo

volver Divisor(); }, ); }, ),
botón de acción flotante:
ScopedModelDescendant<Modelo
de lista de gastos> (constructor:
(contexto, hijo, gastos) { return
Botón de acción flotante (onPressed: () {

Navigator.push( contexto,
MaterialPageRoute( constructor: (contexto) =>
ScopedModelDescendant<ExpenseListModel>( constructor:
(contexto, niño, gastos) { return FormPage( id: 0,

gastos: gastos, ); }))); //


gastos.add(nuevo Gasto( //
2, 1000,
DateTime.parse('2019-04-01 11:00:00'),

'Comida'));
// imprimir(gastos.artículos.longitud); },
información sobre herramientas: 'Incremento',
hijo: Icon(Icons.add), ); }));

}
}

clase FormPage extiende StatefulWidget {


FormPage({Clave clave, este.id, este.gastos}) : super(clave: clave);

identificación
interna final; gastos finales de ExpenseListModel;

@anular
_FormPageState createState() => _FormPageState(id: id, gastos: gastos); }

clase _FormPageState extiende Estado<FormPage> {


_FormPageState({Clave clave, this.id, this.expenses});

identificación
interna final; gastos finales de ExpenseListModel;

scaffoldKey final = GlobalKey<ScaffoldState>(); formKey final =


GlobalKey<FormState>();

doble _cantidad;
FechaHora _fecha;
Cadena _categoría;

174
Machine Translated by Google

Aleteo

vacío _enviar() {
formulario final = formKey.currentState;

if (formulario.validar())
{ formulario.guardar();

si (este.id == 0)
gastos.add(Gasto(0, _cantidad, _fecha, _categoría)); else
gastos.update(Expense(this.id, _amount, _date, _category));

Navigator.pop(contexto);
}
}

@anular
Creación de widgets (contexto BuildContext)
{ return Scaffold (
clave: scaffoldKey,
appBar: AppBar(título:
Texto('Ingresar detalles de gastos'), cuerpo:
Padding( relleno: const EdgeInsets.all(16.0), child:
Form( key: formKey, child: Column( children:
[ TextFormField( estilo: TextStyle(fontSize: 22),
decoración: const InputDecoration( icono: const
Icon(Icons.monetization_on), labelText:
'Cantidad', labelStyle: TextStyle(fontSize:

18)),
validador: (val) {
Patrón patrón = r'^[1-9]\d*(\.\d+)?$'; RegExp
expresión regular = new RegExp(patrón); if (!
regex.hasMatch(val)) devuelve 'Ingrese un número
válido'; demás

devolver nulo;
},
valor inicial:
identificación == 0 ? '' : gastos.byId(id).amount.toString(), onSaved:

(val) => _amount = double.parse(val), ), TextFormField( estilo:


TextStyle(fontSize: 22), decoración: const InputDecoration(

icon: const Icon(Icons.calendar_today), hintText:


'Ingrese la fecha', labelText: 'Date', labelStyle:
TextStyle(fontSize: 18), validator: (val) {

175
Machine Translated by Google

Aleteo

Patrón patrón =
r'^((?:19|20)\d\d)[- /.](0[1-9]|1[012])[- /.](0[1- 9]| [12][0-9]|3[01])$';

RegExp expresión regular = new


RegExp(patrón); si (!regex.hasMatch(val))
return 'Ingrese una fecha válida'; de lo
contrario, devuelve nulo;

},
onSaved: (val) => _date = DateTime.parse(val), initialValue:
id == 0 ? '' gastos.byId(id).formattedDate,
: keyboardType:
TextInputType.datetime, ), TextFormField( estilo: TextStyle(fontSize: 22),
decoración: const InputDecoration( icon: const
Icon(Icons.category), labelText:

'Categoría', labelStyle: TextStyle(fontSize: 18)), onSaved:


(val) => _category = val, initialValue: id ==
0 ? ''
: gastos.byId(id).category.toString(),
),

RaisedButton( onPressed:
_submit, child: new
Text('Submit'), ), ], ), ), ), );

}
}

• Ahora, ejecute la aplicación.

• Agregar nuevos gastos usando el botón flotante.


• Edite los gastos existentes tocando la entrada de gastos
• Elimine los gastos existentes deslizando la entrada de gastos en cualquier dirección.

176
Machine Translated by Google

Aleteo

Algunas de las capturas de pantalla de la aplicación son las siguientes:

177
Machine Translated by Google

Aleteo

178
Machine Translated by Google

Aleteo

179
Machine Translated by Google

21. Aleteo – Conclusión Aleteo

Flutter framework hace un gran trabajo al proporcionar un excelente marco para crear aplicaciones
móviles de una manera verdaderamente independiente de la plataforma. Al proporcionar simplicidad
en el proceso de desarrollo, alto rendimiento en la aplicación móvil resultante, interfaz de usuario
rica y relevante para la plataforma Android e iOS, Flutter framework seguramente permitirá a muchos
nuevos desarrolladores desarrollar aplicaciones móviles de alto rendimiento y con todas las funciones
en el futuro cercano.

180

También podría gustarte