Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

4 Flutter-For-Dummies - Compress-157-284 - 049-115-.En - Es

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 67

Traducido del inglés al español - www.onlinedoctranslator.

com

36,0en el _construirfiladetres(ver Listado 6-9) tienen alguna influencia en las alturas de


las cajas.

Con un poco de ajuste, el código del Listado 6-9 puede proporcionar más evidencia de que un
Expandidowidget no es necesariamente un widget grande. Prueba estos dos experimentos:

1.Vuelva a ejecutar el código de los Listados 6-1 y 6-9. Pero en elbuildRoundedBox


declaración de método, cambioancho: 88.0aancho: 130.0.

En mi simulador de iPhone 11 Pro Max, los anchos de las cajas Giraffe y Wombat son de
130,0 dp cada una. Pero el ancho de laExpandidoLa caja Store Manager tiene solo 94,0
dp. Las cajas Giraffe y Wombat son bastante grandes. Por lo tanto, cuando el cuadro
Store Manager llena el espacio disponible restante, ese espacio tiene solo 94,0 dp de
ancho. (Vea la Figura 6-15.)

FIGURA 6-15:
Expansión para adaptarse

en un pequeño
espacio.

2.En elbuildRoundedBoxdeclaración de método, cambioanchode su valor en el


Paso 1 (ancho: 130.0) aancho: 180.0.

Con las cajas Giraffe y Wombat y elTamaño de la cajawidgets que ocupan 380.0 dp, no
queda espacio en mi simulador de iPhone 11 Pro Max para el cuadro Store Manager.
¡Pobre de mí! Veo la franja negra y amarilla, que indica desbordamiento de RenderBox.
(Consulte la Figura 6-16).Expandidowidget no es un hacedor de milagros. No ayuda a
resolver todos los problemas.

FIGURA 6-16:
más barricada
cinta.

CAPÍTULO 6Colocar las cosas 191


El widget ampliado salva el día
Los listados 6-10 y 6-11 ilustran una situación desagradable que puede surgir cuando mezcla
filas y columnas en varios niveles.

LISTADO 6-10: Una lista condenada al fracaso

// App0610.dardo --CÓDIGO MALO

importar 'paquete: flutter/material.dart';

importar 'App06Main.dart'; importar


'restricciones_logger.dart';

Widget buildColumn (contexto BuildContext) {


fila de retorno (
niños: [
_construirFilaDeTres(),
],
);
}

Widget _buildRowOfThree() {
devuelve ConstraintsLogger(
comentario: 'En _buildRowOfThree',
hijo: Fila(
niños: <Widget>[
_buildExpandedBox("Gato"),
_buildExpandedBox("Perro"),
_buildExpandedBox("Mono"),
],
),
);
}

Widget _buildExpandedBox(
Etiqueta de cadena, {
doble altura = 88.0, }) {

volver Expandido(
hijo: buildRoundedBox(
etiqueta,

altura: altura,
),
);
}

192 PARTE 2Aleteo: una vista de Burd's-Eye


LISTADO 6-11: Una ayuda para la depuración

// restricciones_logger.dart

importar 'paquete: flutter/material.dart';

class ConstraintsLogger extiende StatelessWidget {


comentario de cadena final;
Widget final hijo;

Registrador de restricciones ({

este.comentario = "",
@requerido este.niño,
}): afirmar (comentario! = nulo);

Compilación del widget (contexto BuildContext) {


devuelve LayoutBuilder(
constructor: (contexto BuildContext, restricciones BoxConstraints) {
print('$comentario: $restricciones a ${child.runtimeType}'); hijo de
vuelta;
},
);
}
}

Cuando ejecuta el código de los Listados 6-10 y 6-11, suceden tres cosas:

»No aparece nada en la pantalla de su dispositivo, excepto tal vez un gris opaco
antecedentes.

»En la ventana de la herramienta Ejecutar de Android Studio, verá el siguiente mensaje de error:

Los hijos de RenderFlex tienen una flexión distinta de cero pero un ancho entrante

las restricciones son ilimitadas.

Los desarrolladores de Flutter comienzan a gemir cuando ven este mensaje.

Posteriormente, en la ventana de la herramienta Ejecutar. . .

Si un padre va a envolver en plástico a su hijo, el niño


no puede expandirse simultáneamente para adaptarse a su padre.

»Además, en la ventana de la herramienta Ejecutar, verá un mensaje como este:

yo/aleteo(5317): En _buildRowOfThree:
BoxConstraints(0.0<=w<=Infinito, 0.0<=h<=683.4) a Fila

CAPÍTULO 6Colocar las cosas 193


Esteyo/aleteoel mensaje le dice que la fila interior del diseño está recibiendo una
restricción de ancho que tiene algo que ver conInfinidad.este informativo
0.0<=w<=InfinitoEl mensaje le llega por cortesía del código del Listado 6-11.

¿Qué significan todos estos mensajes? En una aplicación Flutter, tus widgets forman un árbol.
La Figura 6-17 muestra un árbol de widgets tal como se muestra en Flutter Inspector de
Android Studio.

FIGURA 6-17:
El árbol creado
por Listados 6-10
y 6-11.

Para mostrar tus widgets, Flutter viaja en dos direcciones:

»A lo largo del árbol de arriba a abajo


Durante este viaje, cada widget le dice a sus hijos qué tamaños pueden tener. En la
terminología de Flutter, cada widget principalpasa restriccionesa sus hijos.

Por ejemplo, un mensaje de la ventana de la herramienta Ejecutar dice que, en el


Listado 6-11, la fila exterior pasa la restricción de ancho de0.0<=w<=Infinitoa la fila
interior. por la palabraInfinidad,esta restricción se llamarestricción ilimitada.

Si está buscando un ejemplo de unrestricción acotada, mire el mismo mensaje de la


ventana de la herramienta Ejecutar. La fila exterior pasa la restricción de altura de

194 PARTE 2Aleteo: una vista de Burd's-Eye


0,0<=h<=683,4a la fila interior. Esa restricción está limitada por el valor
683.4 dp.

Eventualmente, Flutter llega al final del árbol de widgets de su aplicación. En ese


punto . . .

»A lo largo del árbol otra vez, esta vez, de abajo hacia arriba
Durante este viaje, cada widget hijo le dice a su padre exactamente qué tamaño
quiere tener. El padre recopila esta información de cada uno de sus hijos y utiliza la
información para asignar posiciones a los hijos.

A veces esto funciona bien, pero en el Listado 6-11 falla estrepitosamente.

En el Listado 6-11, debido a que cada caja de animales está dentro de unExpandido
widget, la fila interior no sabe qué tan grande debe ser. La fila interior debe tener un
ancho para dividir el espacio entre las cajas de animales. Pero la fila exterior ha dado una
restricción ilimitada a la fila interior. En lugar de decirle a la fila interna su ancho, la fila
externa le pregunta a la fila interna cuál es su ancho. Nadie quiere asumir la
responsabilidad, por lo que Flutter no sabe qué hacer. (Vea la Figura 6-18.)

FIGURA 6-18:
mi primer gráfico
novela.

¿Cómo puedes solucionar este desagradable problema? Curiosamente, otroExpandidowidget


viene al rescate.

Widget _buildRowOfThree() {
devolverExpandido(
hijo: ConstraintsLogger(
comentario: 'En _buildRowOfThree',
niño:Fila(
niños: <Widget>[

CAPÍTULO 6Colocar las cosas 195


_buildExpandedBox("Gato"),
_buildExpandedBox("Perro"),
_buildExpandedBox("Simio"),
],
),
),
);
}

este nuevoExpandidowidget pasa restricciones limitadas por el árbol de widgets, como puede ver en
este nuevo mensaje en la ventana de la herramienta Ejecutar:

I/flutter (5317): En _buildRowOfThree:


BoxConstraints(w=371.4, 0.0<=h<=683.4) a Fila

El nuevoExpandidowidget le dice a la fila interior que su ancho debe ser exactamente 371.4 dp,
por lo que la confusión que se ilustra en la Figura 6-18 desaparece. Flutter sabe cómo mostrar
los widgets de la aplicación, y ves tres cuadros de animales bien dispuestos en la pantalla de tu
dispositivo. ¡Problema resuelto!

la restricciónw=371.4se llama unrestricción estrictaporque le da a la fila un tamaño


exacto sin margen alguno. Por el contrario, la restricción0,0<=h<=683,4se llama un
restricción suelta. La restricción suelta dice: “Sé tan corto como 0.0 dp de alto y tan alto
como 683.4 dp de alto. Mira si me importa."

Este negocio con restricciones y tamaños puede parecer demasiado complicado. Pero el
proceso de escanear el árbol hacia abajo y luego hacia arriba es una parte importante del
marco Flutter. El enfoque de dos escaneos permite una reconstrucción eficiente de los widgets
con estado. Y la reconstrucción de widgets con estado es fundamental para la forma en que se
diseñan las aplicaciones de Flutter.

Algunos esquemas de diseño funcionan bien con una pequeña cantidad de componentes, pero
comienzan a ralentizarse cuando la cantidad de componentes aumenta. El esquema de diseño de
Flutter funciona bien con solo unos pocos widgets y se escala muy bien para diseños complicados con
una gran cantidad de widgets.

losRegistrador de restriccioneswidget es solo para fines de depuración. Antes de publicar una


aplicación, elimine todos los usos de laRegistrador de restriccionesde su código.

Flexionando algunos músculos

Usando FlutterExpandidowidget, puede especificar los tamaños relativos de los niños dentro
de una columna o una fila. El listado 6-12 tiene un ejemplo.

196 PARTE 2Aleteo: una vista de Burd's-Eye


LISTADO 6-12: Cómo especificar tamaños relativos

// App0612.dardo

importar 'paquete: flutter/material.dart';

importar 'App06Main.dart';

Widget buildColumn (contexto BuildContext) {


columna de retorno (

mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch, children:
<Widget>[
construirTituloTexto(),
SizedBox(altura: 20.0),
_buildRowOfThree(),
],
);
}

Widget _buildRowOfThree() {
fila de retorno (
mainAxisAlignment: MainAxisAlignment.spaceBetween, hijos:
<Widget>[
_construirCuadroExpandido(

"Alce",
),
_construirCuadroExpandido(

"Ardilla",
flexión: 1,

),
_construirCuadroExpandido(

"Dinosaurio",
flexión: 3,

),
],
);
}

Widget _buildExpandedBox(
Etiqueta de cadena, {
doble altura = 88.0, flexión
interna,
}) {
volver Expandido(
flexionar: flexionar,

hijo: buildRoundedBox(

CAPÍTULO 6Colocar las cosas 197


etiqueta,

altura: altura,
),
);
}

¿Qué pasará con nuestros héroes, el alce y la ardilla, en el Listado 6-12? Para averiguarlo,
consulte la Figura 6-19.

FIGURA 6-19:
la ardilla es
pequeña; la
el dinosaurio es grande.

Note el uso frecuente de la palabraflexionaren el Listado 6-12. UnExpandidowidget puede tener un


flexionarvalor, también conocido como factor de flexión. Afactor de flexióndecide cuánto espacio
consume el widget en relación con los otros widgets en la fila o columna.

El listado 6-12 tiene tres cajas:

»Moose, sin valor flexible (el valornulo)


»Ardilla, con valor de flexión 1
»Dinosaurio, con valor de flexión 3

Aquí está la verdad sobre el tamaño resultante de cada caja:

Porque la caja Moose tiene unnulovalor flexible, el cuadro Moose tiene cualquier ancho que
provenga explícitamente de _buildExpandedBoxmétodo. El ancho de la caja Moose es 88.0.
(Consulte la Figura 6-19.)

Tanto los cuadros de ardilla como los de dinosaurio tienen valores de flexión distintos de cero y no
nulos. Entonces esas dos cajas comparten el espacio que queda después de que la caja Moose está en
su lugar. Con valores flexibles de Ardilla: 1, Dinosaurio: 3, el cuadro Dinosaurio es tres veces el ancho
del cuadro Ardilla. En mi emulador Pixel 2, la caja de ardilla tiene 70,9 dp de ancho y la caja de
dinosaurio tiene 212,5 dp de ancho. Así es como funcionan los valores flexibles.

198 PARTE 2Aleteo: una vista de Burd's-Eye


Además deExpandidowidgetsflexionarpropiedad, Flutter tiene clases llamadas
FlexionaryFlexible.Es fácil confundirlos a los tres. CadaFlexionarinstancia es unFila
instancia o unColumnainstancia. Y cadaExpandidoinstancia es una instancia de la
Flexibleclase. AFlexibleinstancia puede tener unflexionarvalor, pero un
Flexibleinstancia no obliga a su hijo a llenar el espacio disponible. ¡Qué hay sobre eso!

¿Qué tan grande es mi dispositivo?

El título de esta sección es una pregunta, y la respuesta es “No sabes”. Puedo ejecutar una aplicación
de Flutter en un iPhone 6 pequeño o en una página web en una pantalla de 50 pulgadas. Quiere que
su aplicación se vea bien sin importar el tamaño de mi dispositivo. Como puedes hacer eso? El listado
6-13 tiene una respuesta.

LISTADO 6-13: Comprobación de la orientación del dispositivo

// App0613.dardo

importar 'paquete: flutter/material.dart';

importar 'App06Main.dart';

Widget buildColumn(contexto) {
if (MediaQuery.of(contexto).orientación == Orientación.paisaje) {
volver _buildOneLargeRow(); }
más {
return _construirDosFilasPequeñas();
}
}

Widget_construir una fila grande (){


columna de retorno (

mainAxisAlignment: MainAxisAlignment.center, niños:


<Widget>[
Fila(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, niños:
<Widget>[
buildRoundedBox("Aardvark"),
buildRoundedBox("Babuino"),
buildRoundedBox("Unicornio"),
buildRoundedBox("Eel"),
buildRoundedBox("Emu"),
buildRoundedBox("Ornitorrinco"),
],

CAPÍTULO 6Colocar las cosas 199


),
],
);
}

Widget_construirDosFilasPequeñas(){
columna de retorno (

mainAxisAlignment: MainAxisAlignment.center, niños: [

Fila(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, niños: [

buildRoundedBox("Aardvark"),
buildRoundedBox("Babuino"),
buildRoundedBox("Unicornio"),
],
),
SizedBox(
altura: 30.0,
),
Fila(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, niños: [

buildRoundedBox("Anguila"),
buildRoundedBox("Emu"),
buildRoundedBox("Ornitorrinco"),
],
),
],
);
}

Las Figuras 6-20 y 6-21 muestran lo que sucede cuando ejecuta el código del Listado 6-13.
Cuando el dispositivo está en modo vertical, verá dos filas, con tres cuadros en cada fila.
Pero cuando el dispositivo está en modo horizontal, solo ve una fila, con seis cuadros.

La diferencia surge por lasideclaración en el Listado 6-13.

if (MediaQuery.of(contexto).orientación == Orientación.paisaje) {
volver _buildOneLargeRow();
} más {
return _construirDosFilasPequeñas();

200 PARTE 2Aleteo: una vista de Burd's-Eye


FIGURA 6-20:
Listado 6-13 en
modo retrato.

FIGURA 6-21:
Listado 6-13 en
Modo paisaje.

Sí, el lenguaje de programación Dart tiene unsideclaración. Funciona de la misma manera quesi
Las sentencias funcionan en otros lenguajes de programación.

si (cierta condición es verdadera) {


haz estas cosas;

CAPÍTULO 6Colocar las cosas 201


}de lo contrario{
Haz estas otras cosas;
}

En el nombreMediaQuery,la palabraMedios de comunicaciónse refiere a la pantalla que


ejecuta su aplicación. Cuando usted llamaMediaQuery.of(contexto),obtienes un tesoro de
información sobre esa pantalla, como

»orientación:Si el dispositivo está en modo vertical u horizontal


»tamaño.alturaytamaño.ancho:El número de unidades dp de arriba a abajo
y en la pantalla del dispositivo

»size.longestSideytamaño.lado más corto:La pantalla más grande y más pequeña


valores de tamaño, independientemente de cuál es la altura y cuál es el ancho

»tamaño.relación de aspecto:El ancho de la pantalla dividido por su altura.


»relación de píxeles del dispositivo:El número de píxeles físicos para cada unidad de dp
»relleno, viewInsets,yRelleno de vista:Las partes de la pantalla que no son
disponible para el desarrollador de la aplicación Flutter, como las partes cubiertas por la
muesca del teléfono o (a veces) el teclado virtual

»utilizar siempre el formato de 24 horas:La configuración de visualización de la hora del dispositivo

»plataformaBrillo:La configuración de brillo actual del dispositivo


». . .y muchos más

Por ejemplo, una tableta Pixel C con 2560 x 1800 dp es lo suficientemente grande como para mostrar
una fila de seis cajas de animales en modo vertical u horizontal. Para prepararse para que su
aplicación se ejecute en dicho dispositivo, es posible que no desee confiar en el dispositivoorientación
propiedad. En ese caso, puede reemplazar la condición del Listado 6-13 con algo como lo
siguiente:

si (MediaQuery.of(contexto).tamaño.ancho >= 500.0) {


volver _buildOneLargeRow();
} más {
return _construirDosFilasPequeñas();

Fíjate en la palabracontextoen el códigoMediaQuery.of(contexto).Para consultar los medios,


Flutter debe conocer el contexto en el que se ejecuta la aplicación. Es por eso que, comenzando
con la primera lista de este capítulo, el _Mi página de iniciode claseconstruir
método tiene uncontexto BuildContextparámetro. El Listado 6-1 tiene esta llamada de método:

construirColumna(contexto)

202 PARTE 2Aleteo: una vista de Burd's-Eye


Y otros listados tienen declaraciones de métodos con este encabezado:

Widget buildColumn(contexto BuildContext)

Los listados 6-2 a 6-12 no hacen uso de esocontextoparámetro. Pero, ¿y si, en el Listado
6-1, omito el métodocontextoparámetro, así:

construirColumna()

Entonces todo marcha sobre ruedas hasta el Listado 6-13, que no tiene acceso a la
contextoy no puede llamarMediaQuery.of(contexto).¡Qué pena!

Cuando creé el Listado 6-1, agregué elcontextoparámetro porque anticipé la necesidad


delcontextoen el último listado de este capítulo — Listado 6-13. Sí, soy un tipo muy
inteligente.

Bueno, eso no es realmente cierto. Cuando comencé a escribir este capítulo, no anticipé la
necesidad de lacontextovalor. no vi elcontextoproblema que venía hasta que comencé a
escribir esta última sección. En ese momento, volví y modifiqué cada uno de los listados para
que elcontextoestaría disponible para el Listado 6-13. ¡Oh bien! Todo el mundo tiene que hacer
correcciones de rumbo. Es parte de la vida, y ciertamente es parte del desarrollo de
aplicaciones profesionales.

En el próximo capítulo. . . .

CAPÍTULO 6Colocar las cosas 203


3
Detalles, Detalles
EN ESTA PARTE . . .

Respondiendo a la entrada del usuario

Disposición de los componentes de una aplicación

Navegando de una página a otra

Creación de animación
EN ESTE CAPÍTULO

»Recopilación de respuestas del usuario

»Respondiendo a la entrada

»Tratar con valores nulos

»Consejos sobre el amor y el matrimonio.

Capítulo7
Interactuando con el Usuario

L ¡El amor está en el aire! El sol está brillando. Los pájaros están cantando. Mi corazón es todo un aleteo.
(Juego de palabras intencionado).

Doris D. Developer quiere encontrar pareja y tiene dos criterios importantes. Primero, quiere a
alguien que tenga 18 años o más. En segundo lugar, está buscando a alguien a quien le
encante desarrollar aplicaciones de Flutter. ¿Qué mejor manera para que Doris logre su
objetivo que escribir su propia aplicación de citas?

Este capítulo cubre el destacado trabajo de Doris. Para crear la aplicación, Doris usa varios tipos
de widgets: un campo de texto, un control deslizante, un botón desplegable y algunos otros. Un
widget de este tipo, uno que el usuario ve y con el que interactúa, se llamaelemento de control,
o simplemente uncontrol.

La aplicación de Doris también tiene algunos widgets de diseño, comocentro, fila,yColumna,


pero estos widgets de diseño no se llamancontrol S.El usuario realmente no los ve y
ciertamente no interactúa con ellos. El énfasis de este capítulo está en los controles, no en los
widgets de diseño ni en otras partes variadas de la aplicación.

La última aplicación de citas de Doris no tiene todas las funciones según los estándares comerciales, pero el
código de la aplicación tiene unos cientos de líneas. Es por eso que Doris desarrolla la aplicación en partes
pequeñas: primero un control, luego otro, y otro, y así sucesivamente. Cada pieza es una pequeña aplicación
de práctica independiente.

La primera aplicación de práctica trata con una pregunta simple: ¿Tiene la futura pareja al
menos 18 años?

CAPÍTULO 7Interactuando con el Usuario 207


Un cambio simple
ACambiares un control que está en uno de dos estados posibles: encendido o apagado, sí o no,
verdadero o falso, feliz o triste, mayor de 18 años o no. Listado 7-1 tiene el código para la práctica
Cambiaraplicación

LISTADO 7-1: ¿Cuantos años tienes?

importar 'paquete: flutter/material.dart';

vacío principal() => ejecutarApp(App0701());

clase App0701 extiende StatelessWidget {


@anular
Compilación del widget (contexto BuildContext) {
devolver MaterialApp(
inicio: MiPáginaInicio(),
);
}
}

clase MyHomePage extiende StatefulWidget {


@anular
_MyHomePageState createState() => _MyHomePageState();
}

const _eres = 'Tú eres';


const _compatible = 'compatible con\nDoris D. Developer.';

class _MyHomePageState extiende State<MyHomePage> {


bool _ageSwitchValue = falso;
String _messageToUser = "$_usted NO es $_compatible";

/// Estado

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

title: Text("¿Eres compatible con Doris?"),


),
cuerpo: relleno (
relleno: const EdgeInsets.all(8.0), hijo:
Columna(
niños: <Widget>[
_buildAgeSwitch(),

208 PARTE 3Detalles, Detalles


_buildResultArea(),
],
),
),
);
}

/// Construir

Widget _buildAgeSwitch() {
fila de retorno (
niños: <Widget>[
Texto ("¿Tienes 18 años o más?"),
Cambiar(
valor: _ageSwitchValue,
onChanged: _updateAgeSwitch,
),
],
);
}

Widget _buildResultArea() {
devolver texto (_ mensaje a usuario, textAlign: TextAlign.center);
}

/// Acciones

void _updateAgeSwitch(bool nuevoValor) {


establecerEstado(() {

_ageSwitchValue = nuevoValor;
_mensaje al usuario =
_youAre + (_ageSwitchValue ? " " : " NOT ") + _compatible;
});
}
}

Las Figuras 7-1 y 7-2 muestran la aplicación en sus dos estados posibles.

Las listas de este capítulo son aplicaciones de práctica. Son muestras del tamaño de un bocado de la
gran y hermosa aplicación de citas de Doris. Pero incluso los programas "pequeños" pueden ser largos
y complicados. Para que las listas de este capítulo sean breves, reutilizo el código de una lista a otra.
Doblo algunas reglas e ignoro algunas buenas prácticas de programación para hacer que los listados
sean compatibles entre sí. Pero no te preocupes. Cada lista funciona correctamente y cada lista ilustra
conceptos útiles de desarrollo de Flutter. Puede encontrar la aplicación de citas completa en el
Apéndice A y en el material que descargue del sitio web de este libro.
(www.allmycode.com/Flutter).

CAPÍTULO 7Interactuando con el Usuario 209


FIGURA 7-1:
El usuario enciende
el interruptor.

FIGURA 7-2:
El usuario se apaga
el interruptor de nuevo.

El código del Listado 7-1 no es muy diferente del código del Capítulo 5. En el Capítulo 5, el
botón de acción flotante tiene unonPressedparámetro. En el Listado 7-1, elCambiar
widget tiene algo similar. El listado 7-1 tiene unenCambiadoparámetro. losenCambiado
el valor del parámetro es una función; es decir, el _actualizarAgeSwitch
función. Cuando el usuario acciona el interruptor, ese giro activa el interruptor.enCambiado
evento, lo que hace que el marco Flutter llame al _actualizarAgeSwitchfunción.

A diferencia de las funciones de manejo de eventos del Capítulo 5, el _actualizarAgeSwitchfunción en el


Listado 7-1 no es unDevolución de llamada vacía.ADevolución de llamada nulaLa función no toma
parámetros, pero el _actualizarAgeSwitchLa función tiene un parámetro. El nombre del parámetro
esnuevo valor:

void _updateAgeSwitch(bool nuevoValor)

Cuando el marco Flutter llama _actualizar cambio de edad,el marco pasa el


Cambiarnueva posición del widget (activado o desactivado) a lanuevo valorparámetro. Porque
el tipo denuevo valoresbool, valor nuevoes cualquierafalsooverdadero.Esfalsocuando
el interruptor está apagado yverdaderocuando el interruptor está encendido.

Si _actualizarAgeSwitchno es unDevolución de llamada vacía,¿qué es? (Esa era una pregunta


retórica, así que te la respondo. . . .) El _actualizarAgeSwitchla función es de tipo
ValorCambiado<bool>.AValorCambiadofunción toma un parámetro y devuelve
vacío.El parámetro de la función puede ser de cualquier tipo, pero unValorCambiado<bool>
el parámetro de la función debe ser de tipobool.De la misma manera, unValorCambiado<doble>
el parámetro de la función debe ser del tipodoble.Y así.

210 PARTE 3Detalles, Detalles


No se equivoque al respecto: aunque el términoValorCambiado<bool>no tiene la palabraLlamar de
vueltaen ella, el _actualizarAgeSwitchla función es una devolución de llamada. Cuando el usuario
voltea laCambiarwidget, el marco Flutter llama a su código de vuelta. Sí el _actualizarAgeSwitchla
función es una devolución de llamada. simplemente no es unDevolución de llamada vacía.

Con muchos controles, no pasa mucho si no cambia el valor del control y llama
establecerEstado.Por algunas risas, traté de comentar elestablecerestado
llamar en el cuerpo del _actualizarAgeSwitchfunción en el Listado 7-1:

void _updateAgeSwitch(bool nuevoValor) {


// establecerEstado(() {

_ageSwitchValue = nuevoValor;
_messageToUser = _youAre + (_ageSwitchValue ? " " : " NOT ") + _compatible; // });

Luego descomenté elestablecerestadollamar y comentar las declaraciones de


asignación:

void _updateAgeSwitch(bool nuevoValor) {


establecerEstado(() {

// _ageSwitchValue = nuevoValor;
// _mensaje al usuario =

// _youAre + (_ageSwitchValue ? " " : " NOT ") + _compatible;


});
}

En ambos casos, reinicié el programa y luego toqué el interruptor. No solo el _mensaje al


usuarionegarse a cambiar, pero el interruptor ni siquiera se movió. ¡Eso lo resuelve! El
aspecto del interruptor depende completamente del _edadSwitchValue
variable y la llamada aestablecerEstado.Si no le asignas nada a _valor de cambio de
edado no llamasestablecer estado,el interruptor no responde por completo.

Palabra clave const de Dart


Aquí está mi regla cardinal: una vez que he tomado una decisión, nunca cambio de opinión. La
única excepción a esta regla es cuando cambio de opinión sobre la regla cardinal.

En el desarrollo de aplicaciones, el tema del cambio es muy importante. El término


variable viene de la palabravariar,que significa "cambio". Pero algunas cosas no deberían
cambiar. En el Listado 7-1, me refiero a las cadenas 'Usted está'y 'compatible con\
nDoris D. Developer'más de una vez, así que creo nombres prácticos _usted está
y _compatiblepara estas cuerdas. De esa manera, no tengo que escribir cosas como
'compatible con\nDoris D. Developer'mas de una vez. no me arriesgo a escribir
la frase correctamente una vez e incorrectamente otra vez.

CAPÍTULO 7Interactuando con el Usuario 211


Pero, ¿y si el valor de _usted estáse permite cambiar a lo largo de la ejecución del programa?
Un desarrollador que esté trabajando con mi código podría escribir por error

_eres = 'dulce';

No quiero que eso suceda. Deseo _usted estáreposar durante 'Usted está'a lo largo de la
ejecución del programa. Android Studio debería marcar la asignación _eres = 'dulce'
como un error Por eso, en el Listado 7-1, declaro _usted estácon la palabra
constanteDardosconstantepalabra clave es la abreviatura deconstante.Como constante, el valor de
_usted estáno puede cambiar. Lo mismo es válido para la declaración de _compatible
en el Listado 7-1. El uso de DartconstanteLa palabra clave es una medida de seguridad, ¡y es
muy buena!

En caso de que te lo estés preguntando, \norteen 'compatible con\nDoris D. Developer'dice


Dart para ir a una nueva línea de texto. De esa manera,Doris D. Desarrolladoraparece en una línea
propia. (Consulte las Figuras 7-1 y 7-2). La combinación de caracteres \nortese llama un secuencia de
escape.

Refiriéndose al código del Listado 7-1, un desarrollador experimentado podría decir, “el _
usted estáconstante” o “la _usted estávariable." El primero es más preciso, pero el
segundo es aceptable.

Dart tiene dos palabras clave para indicar que ciertas cosas no deberían cambiar:constantey
final.losconstanteLa palabra clave dice: "No cambie este valor en ningún momento durante la
ejecución de la aplicación". losfinalLa palabra clave dice: "No cambie este valor a menos que
encuentre esta declaración nuevamente". La diferencia entreconstanteyfinaltiene muchas
consecuencias sutiles, así que no abro esa lata de gusanos en este capítulo. En su lugar, simplemente
recuerde que los programas conconstantepuede ejecutarse un poco más rápido que los programas
confinal.Puedes poner cualquier viejoconstantedeclaración en el nivel superior de su código o dentro
de una declaración de función. Pero, por unconstanteal comienzo de una clase, la historia es
diferente. El siguiente código es ilegal:

// No hagas esto:
class _MyHomePageState extiende State<MyHomePage> {
const _eres = 'Tú eres';

Pero este código está bien:

Prod: Tenga en cuenta el código en negrita.-BB

// Haz esto en su lugar:

class _MyHomePageState extiende State<MyHomePage> {


static const _youAre = 'Usted es';

212 PARTE 3Detalles, Detalles


Para la verdadera primicia sobre Dart'sestáticopalabra clave, consulte la sección "Llamada 4", más adelante en
este capítulo.

¿Compatibles o NO?
Para algunos usuarios, la aplicación de citas debería decir: "Eres compatible con Doris D.
Developer". Para otros usuarios, la aplicación debe agregarNOa su mensaje. Es por eso que el
Listado 7-1 contiene el siguiente código:

_mensaje al usuario =

_eres + (_ageSwitchValue? " " : " NO ") + _compatible;

La expresion _valorcambioedad? " " : " NO "es unexpresión condicional, y la


combinación de ? y : en esa expresión es de Dartoperador condicional. La figura 7-3 le
muestra cómo Dart evalúa una expresión condicional.

FIGURA 7-3:
evaluando un
condicional
expresión.

Una expresión condicional se ve así:

condición?expresión1:expresión2

Cuando elcondiciónesverdadero,el valor de la expresión completa es lo que encuentres


en elexpresión1parte. Pero, cuando elcondiciónesfalso,el valor de la expresión completa
es lo que encuentres en elexpresión2parte.

Además de sus expresiones condicionales, Dart tienesideclaraciones. Una expresión


condicional es como unasideclaración pero, a diferencia de unasiinstrucción, una expresión
condicional tiene un valor. Ese valor se puede asignar a una variable.

CAPÍTULO 7Interactuando con el Usuario 213


Para ilustrar el punto, les doy unasienunciado cuyo efecto es el mismo que el de la
expresión condicional del Listado 7-1:

si (_ageSwitchValue) {
_messageToUser = _eres + " " + _compatible; } más {

_messageToUser = _eres + " NO " + _compatible;


}

Traducido al inglés sencillo, estesideclaración dice:

Si elboolvariable_edadSwitchValuetiene el valorverdadero,
_messageToUser = _eres + " " + _compatible; de lo
contrario
_messageToUser = _eres + " NO " + _compatible;

En algunas situaciones, elegir entre unsideclaración y una expresión condicional es una


cuestión de gusto. Pero en el Listado 7-1, la expresión condicional es una clara ganadora.
Después de todo, unsideclaración no tiene un valor. No puede asignar unsi
declaración a cualquier cosa o agregar unasideclaración a cualquier cosa. Entonces, el código del
siguiente tipo es ilegal:

// ESTE CÓDIGO NO ES VÁLIDO.

_mensaje al usuario =

_eres +

si (_ageSwitchValue) {
" ";
} más {
" NO ";
}+

_compatible;

Otro nombre para el operador condicional de Dart es eloperador ternario.La palabra ternario
significa "tres" y el operador tiene tres partes: una antes del signo de interrogación, una
segunda entre el signo de interrogación y los dos puntos, y una tercera después de los dos
puntos.

¡Espéralo!
Los usuarios de hoy están impacientes. Quieren retroalimentación instantánea. ¿Cómo sé esto? Un
montón de actores me convencieron. Aquí está la historia:

214 PARTE 3Detalles, Detalles


Hace mucho tiempo, en un teatro muy, muy lejos de Broadway, vi una comedia musical en
compañía de otras nueve personas. Tres de ellos eran mis amigos, dos de ellos miraban desde
los asientos de la tercera fila y los otros cuatro eran los actores de la obra. Recuerdo este
evento por dos razones. Primero, fue el comienzo de mi filosofía de toda la vida sobre hacer
que los artistas se sintieran bien. Los chistes de la obra no eran divertidos, pero me reí a
carcajadas con cada uno de ellos. El canto estaba desafinado, pero aplaudí vigorosamente
después de cada canción. Eventualmente, todos nos estábamos divirtiendo y los actores no se
arrepintieron de haber interpretado ante una audiencia de seis personas.

La otra parte memorable de este evento fue la canción característica de la obra. Era un poco
tonto, pero se quedó grabado en mi mente durante años y años. Hasta el día de hoy, mi esposa
y yo nos reímos cada vez que aullamos los primeros compases de la canción. El título de esta
melodía era "Gratificación inmediata". Era una burla a la cultura moderna, en la que toda
necesidad es urgente y todo deseo debe cumplirse.

Siguiendo esta línea de pensamiento, presento un humilde widget conocido como elBotón levantado.
Un botón no es mucho. Lo presionas y algo sucede. Lo presionas de nuevo, y algo puede suceder o no.
Los botones solían ser el control de acceso para los desarrolladores web y los desarrolladores de
aplicaciones. Pero en estos días, los botones están pasados de moda. Cuando un usuario activa un
interruptor, la aplicación responde de inmediato. No hay que esperar para encontrar un botón para
presionar. El viejo rectángulo gris claro con la palabra Enviaren ella ha pasado a un segundo plano.

En celebración de los buenos viejos tiempos, el ejemplo de esta sección evita la respuesta rápida de la
aplicación en el Listado 7-1. Cuando el usuario acciona un interruptor, el interruptor simplemente se
mueve. La aplicación no dice "Eres compatible" o "No eres compatible" hasta que el usuario presiona
un botón. ¡Ven a sentarte en el porche y relájate mientras se ejecuta esta aplicación! El código está en
el Listado 7-2.

LISTADO 7-2: Responder a una pulsación de botón

// Copie el código hasta e incluyendo el método _buildAgeSwitch //


del Listado 7-1 aquí.

Widget _buildResultArea() {
fila de retorno (
niños: <Widget>[
Botón elevado (
niño: Texto ("Enviar"),
onPressed: _updateResults,
),
SizedBox(
ancho: 15.0,
),
Texto(_mensajeParaUsuario, textAlign: TextAlign.center),
(continuado)

CAPÍTULO 7Interactuando con el Usuario 215


LISTADO 7-2: (continuado)

],
);
}

/// Acciones

void _updateAgeSwitch(bool nuevoValor) {


establecerEstado(() {

_ageSwitchValue = nuevoValor; });

void _updateResults() {
establecerEstado(() {

_messageToUser = 'Usted es' +


(_ageSwitchValue ? " " : " NO ") + 'compatible
con \nDoris D. Developer.';
});
}
}

La Figura 7-4 muestra una instantánea de una ejecución del código del Listado 7-2.

FIGURA 7-4:
¡Buenas noticias!

Cuando se combina con algún código del Listado 7-1, la aplicación del Listado 7-2 tiene ambos
onPressedyenCambiadomanejadores de eventos En particular:

»La función_actualizarAgeSwitchmanejasenCambiadoeventos para el interruptor.


Cuando el usuario toca el interruptor, la apariencia del interruptor cambia de apagado a encendido o de

encendido a apagado.

»La función_actualizarResultadosmanejasonPressedeventos para el botón.


Cuando el usuario presiona el botón, el mensaje de la aplicación se pone al día con el estado del
interruptor. Si el interruptor está encendido, el mensaje se convierte en "Eres compatible". Si el
interruptor está apagado, el mensaje se convierte en "NO eres compatible".

216 PARTE 3Detalles, Detalles


Entre el momento en que el usuario presiona el interruptor y el momento en que el usuario
presiona el botón, el mensaje en la pantalla puede ser inconsistente con el estado del
interruptor. En un formulario en línea con varias preguntas, eso no es un problema. El usuario
no espera ver un resultado hasta después de presionar el botón final. Pero en las aplicaciones
de práctica de este capítulo, cada una con una sola pregunta para el usuario, la falta de
coordinación entre la respuesta del usuario y el mensaje que se muestra es problemática. Estas
aplicaciones de práctica no ganan ningún premio de experiencia de usuario.

Afortunadamente, Doris no publica sus aplicaciones de práctica. En cambio, publica una


aplicación que combina todos los controles de sus aplicaciones de práctica y más.

Puede descargar y ejecutar la aplicación de citas completa de Doris. es el archivo llamado


App_endix.darten la descarga desde el sitio web de este libro. Si necesita una gratificación
inmediata y la descarga del código no es rápida para usted, solo pase las páginas de este libro
hasta llegar al Apéndice A.

¿Qué es lo siguiente? ¡Lo sé! ¿Qué tal un control deslizante?

¿Cuánto amas Flutter?


Doris the Developer quiere conocer a alguien a quien le encanta crear aplicaciones de Flutter. Su aplicación de
citas casera incluye un control deslizante con valores del 1 al 10. Las puntuaciones de 8 y superiores son
aceptables. Cualquiera con una respuesta de 7 o menos puede hacer una caminata.

El Listado 7-3 tiene los aspectos más destacados de la aplicación deslizante de práctica de Doris.

Para ver el resto de la aplicación de práctica de control deslizante de Doris, busque el proyecto
App0703 en la descarga del sitio web de este libro. (¡Rápido! Visitawww.allmycode.com/Flutter
¡de inmediato!)

LISTADO 7-3: Por el amor de Flutter

// Este no es un programa completo. (¡De ninguna manera!)

class _MyHomePageState extiende State<MyHomePage> {


doble _loveFlutterSliderValue = 1.0;

Widget _buildLoveFlutterSlider() {
devolver // ...
Texto("En una escala del 1 al 10, "
"¿Cuánto te gusta desarrollar aplicaciones de Flutter?"),
(continuado)

CAPÍTULO 7Interactuando con el Usuario 217


LISTADO 7-3: (continuado)

Control deslizante (

mín.: 1,0,
máx.: 10,0,
divisiones: 9,
valor: _loveFlutterSliderValue, onChanged:
_updateLoveFlutterSliderValue, etiqueta: '$
{_loveFlutterSliderValue.toInt()}',
),

void _updateLoveFlutterSlider(doble valor nuevo) {


establecerEstado(() {

_loveFlutterSliderValue = nuevoValor; });

void _updateResults() {
establecerEstado(() {

_messageToUser = _eres +
(_loveFlutterSliderValue >= 8 ? " " : " NO ") + _compatible;

});
}
}

La Figura 7-5 muestra una ejecución de la aplicación del control deslizante con el control deslizante configurado hasta el 10. (¿De

qué otra forma espera que configure el control deslizante "love Flutter"?)

FIGURA 7-5:
Amor a primer byte.

218 PARTE 3Detalles, Detalles


losDeslizadorLa llamada al constructor del Listado 7-3 tiene estos seis parámetros:

»min:El valor más pequeño del control deslizante.

El pequeño artilugio que se mueve de izquierda a derecha a lo largo de un control deslizante se llamapulgar. La

posición del pulgar determina la posición del control deslizante.valor. Asi quemines el valor del control deslizante

cuando el pulgar del control deslizante está en el punto más a la izquierda. losminel parámetro tiene tipodoble.

»máximo:El valor más grande del control deslizante.

Este es el valor del control deslizante (nuevamente, undoble)cuando el pulgar está en el punto
más a la derecha.

Los valores de un control deslizante pueden aumentar de izquierda a derecha o de derecha a


izquierda. Antes de mostrar un control deslizante, Flutter verifica undirección del texto
propiedad. Si el valor esDirecciónTexto.ltr,el valor mínimo del control deslizante está a la
izquierda. pero si eldirección del textoel valor de la propiedad esDirecciónTexto.rtl,el valor
mínimo del control deslizante está a la derecha. Uso de aplicaciones escritas para hablantes
de árabe, farsi, hebreo, pastún y urduDirecciónTexto.rtl.Uso de otras aplicaciones
DirecciónTexto.ltr.En caso de que te lo estés preguntando, Flutter no es compatible con la
escritura boustrophedon, un estilo antiguo en el que las líneas alternas fluyen de izquierda a
derecha y luego de derecha a izquierda.

»divisiones:El número de espacios entre los puntos donde se puede colocar el pulgar
metido. (Consulte la Figura 7-6.)

FIGURA 7-6:
porque el numero
de divisiones es 9
en el Listado 7-3.

El control deslizante del Listado 7-3 se puede colocar en los valores 1,0, 2,0, 3,0, etc., hasta 10,0.

Si omite eldivisionesparámetro, o si establece ese parámetro ennulo, el pulgar se puede


colocar en cualquier lugar a lo largo del control deslizante. Por ejemplo, con el siguiente
constructor, el valor del control deslizante puede ser 0,0, 0,20571428571428554,
0,917142857142857, 1,0 o casi cualquier otro número entre 0 y 1.

Control deslizante (

mín.: 0,0,
máx.: 1,0,
valor: _loveFlutterSliderValue,
onChanged: _updateLoveFlutterSlider,
)

CAPÍTULO 7Interactuando con el Usuario 219


»valor:Un número en el rango deminamáx.
Este parámetro determina la posición del pulgar.

»enCambiado:La función de manejo de eventos para cambios en el control deslizante.


Cuando el usuario mueve el pulgar del control deslizante, el marco Flutter llama a esta
función.

»etiqueta:El widget que se muestra en el indicador de valor del control deslizante.


A medida que el usuario mueve el pulgar, aparece una forma adicional. Esa forma es la del
deslizador.indicador de valor. En la Figura 7-5, la burbuja con el número 10 es el indicador de valor
del control deslizante.

A pesar de su nombre, el indicador de valor no muestra necesariamente unTextowidget que muestra el


valor del control deslizante. De hecho, el indicador de valor puede mostrar cualquier cosa que desee que
muestre. (Bueno, casi cualquier cosa).

Afortunadamente para nosotros, el widget en el control deslizante del Listado 7-3


muestra _loveFlutter Valor deslizante —el propio valor del control deslizante. Pero
recuerde: si no desea que aparezcan números como 0.20571428571428554 en el
indicador de valor, debe convertir el control deslizantedoblevalores enEn tvalores. Es por
eso que, en el Listado 7-3, el widget en el indicador de valor del control deslizante
muestra _loveFlutter SliderValue.toInt(),no simple viejo _amorFlutterSliderValue.

Si no especifica unetiquetaparámetro, o si especifica unetiquetapero hazlo


nulo,el indicador de valor nunca aparece.

Tratar con campos de texto


En esta sección, presento al amigo de Doris, Irving. A diferencia de Doris, Irving quiere un compañero
con mucho dinero. Con este fin, Irving le pide a Doris que cree una variación de su aplicación de citas.
La aplicación personalizada de Irving tiene dos campos de texto: uno para el nombre de un usuario y
otro para los ingresos del usuario. Si el ingreso del usuario es de $100,000 o más, la aplicación informa
"compatible". De lo contrario, la aplicación informa "incompatible". La Figura 7-7 tiene una versión
ilustrada del _ de la aplicaciónEstado de mi página de inicioclase. Para ver el resto de la aplicación de
Irving, busque el proyecto App0704 en la descarga del sitio web de este libro.

220 PARTE 3Detalles, Detalles


FIGURA 7-7:
(También conocido

como el Listado 7-4.)


Cuánto
ganas?

Para mantener manejable el tamaño de la Figura 7-7, omití la declaración de


_construirDecoración.En caso de que te lo preguntes, aquí está el código de ese método:

InputDecoration _buildDecoration(etiqueta de cadena) {


return EntradaDecoración(
etiquetaTexto: etiqueta,

borde: EsquemaInputBorder(

CAPÍTULO 7Interactuando con el Usuario 221


borderRadius: BorderRadius.all(Radius.circular(10.0)),
),
);
}

La figura 7-8 muestra el patético intento de Pat de ser considerada compatible con Irving. Con
un ingreso de $61,937, Pat no tiene ninguna oportunidad. (En 2018, el ingreso medio de los
hogares en los Estados Unidos fue de $ 61,937. Las expectativas de Irving son demasiado altas).

FIGURA 7-8:
Malas noticias para Pat.

Los campos de texto tienen los mismos tipos de controladores de eventos que tienen los interruptores y los
controles deslizantes. En particular, unCampo de textoconstructor puede tener unenCambiadocontrolador de
eventos: una función que se ve así:

void _updateStuff(String nuevoValor) {


// Cuando el usuario escribe un caracter, hacer algo con
// los caracteres dentro del campo de texto (el newValue).
}

Pero, ¿qué pasa con la pulsación de un botón? ¿Hay una buena manera de averiguar qué
hay en un campo de texto cuando los caracteres del campo no cambian? Sí hay. Es el
Controlador de edición de texto —una característica sobresaliente en la Figura 7-7.

De hecho, la Figura 7-7 tiene dosControlador de edición de textoobjetos: uno para el


campo Su nombre y otro para el campo Sus ingresos. Los siguientes párrafos agregan
detalles a las llamadas numeradas en la Figura 7-7.

222 PARTE 3Detalles, Detalles


Llamadas 1 y 2
En un programa de Flutter, las llamadas al constructor gobiernan. obtienes unTextowidget con
una llamada de constructor comoTexto("Hola").obtienes unColumnay dosTextowidgets
con código comoColumna (hijos: [Texto ('a'), Texto ('b')]).

Cuando emite una llamada de constructor, la llamada en sí representa un objeto. Por


ejemplo, la llamadaTexto("Hola")representa un particularTextowidget: una instancia del
Textoclase. Puede asignar la llamada a una variable y usar esa variable en otra parte de
su código:

@anular
Compilación del widget (contexto BuildContext) {

Text myTextInstance = Text("Soy reutilizable");


andamio de vuelta(
barra de aplicaciones: barra de aplicaciones (

título:miTextInstance,
),
cuerpo: Columna(

niños: <Widget>[
miTextInstance,
],
),
);
}

En muchos casos, puede separar la declaración de la variable de la llamada:

Texto myTextInstance;

// Más código aquí y en otros lugares...

myTextInstance = Text("Soy reutilizable");

En la Figura 7-7, la declaración de las dos variables del controlador (_controlador de campo de
nombrey _controlador de campo de ingresos)está separado del correspondiente
Controlador de edición de textollamadas al constructor. Hago esto para presentar Flutter's
initStateydisponermétodos.

AEstadoel objeto es como cualquier otra cosa en el mundo: surge y, eventualmente,


desaparece. Llamadas de aleteoinitStatecuando unaEstadola instancia surge y llama
disponercuando elEstadola instancia desaparece.

CAPÍTULO 7Interactuando con el Usuario 223


NULL POUR LES NULS
Puede declarar un nombre de variable sin asignar nada a esa variable. Si lo hace, el valor inicial
de la variable esnulo,que significa “absolutamente nada”. En muchos casos, eso es exactamente
lo que quieres hacer.

Pero tienes que tener cuidado. un no deseadonuloel valor puede ser peligroso. Por ejemplo, el siguiente

código se bloquea como un automóvil imprudente en la autopista de peaje de Nueva Jersey:

principal() {
cantidad int;
print(cantidad.esPar); // null.isEven -- No puedes hacer esto
}

Por otro lado, si asignas algo a lacantidadvariable, el código se ejecuta sin


problemas:

principal() {
cantidad int;
cantidad = 22;
print(cantidad.esPar); // Muestra la palabra "verdadero" (sin comillas)
}

Aquí hay un error que a veces cometo: creo una declaración de variable que no asigna un valor a
su variable. Luego me olvido de asignar un valor a esa variable en otra parte del código. ¡Ups! Mi
código falla. Mi consejo es que intentes no cometer ese error.

Puede que no sea obvio, pero el código de la figura 7-7 hace referencia a dosinitState
métodos. La declaración que comienza convoid initState()describe un método que pertenece al
_Estado de mi página de inicioclase. Pero el _Estado de mi página de iniciola clase extiende
la propia de FlutterEstadoclase y esoEstadola clase tiene lo suyoinitState
declaración. (Vea la Figura 7-9.)

FIGURA 7-9:
Anulando un
clase extendida
método initState.

224 PARTE 3Detalles, Detalles


Cuando tienes dos métodos nombradosestado inicial,como distinguir uno de otro?
Bueno, ¿qué pasa si conoces a una mujer llamada María, cuyo hijo también se llama
María? Lo más probable es que el niño no llame a su madre "María". En cambio, el niño
llama a su madre “mamá” o algo así. Para el cumpleaños de su madre, compra una taza
de recuerdo que muestra las palabrasSúper mamá,y su madre sonríe cortésmente al
recibir otro obsequio inútil.

El mismo tipo de cosas sucede cuando dos clases, un padre y su hijo, tienen métodos llamados
initState.La clase secundaria (_Estado de mi página de inicio)tiene que llamar al
initStatemétodo perteneciente a su clase padre (Flutter'sEstadoclase). Para hacerlo, la
clase secundaria llamasuper.initState().A diferencia de la situación de Mary, el uso de la
palabra clavesúperno pretende ser halagador. Es simplemente una referencia a la
initStatemétodo que está definido en la clase principal. (No puedo resistir: La palabra clave
súperpuede que no sea halagador, pero ciertamente es aleteo).

Para extender un poco más la metáfora de madre e hija, imagina que la Súper Mamá
Mary es una agente de bienes raíces. En ese caso, el niño no puede comprar una casa sin
antes consultar a su madre. Los niñosdecidir cual casamétodo debe incluir una llamada
a la madredecidir cual casamétodo, así:

// La declaración del método del niño:


@anular
void decideQueCasa() {
super.decidirQueCasa();
// Etc.
}

Esa puede ser la situación cuando su código anula el de FlutterinitStatemétodo. En


algunas versiones de Flutter, si no llamassuper.initState(),su código no se ejecutará.

Llamada 3
CadaCampo de textoconstructor puede tener su propiocontroladorparámetro. El controlador de un
campo de texto media el flujo de información entre el campo de texto y otras partes de la aplicación.
(Para obtener más información, salte a la sección posterior "Llamada 4").

En otra parte delCampo de textollamada al constructor, laTextInputType.númeroEl parámetro en el


constructor del campo de texto de ingresos le dice a un dispositivo que muestre un teclado virtual con
solo dígitos en las teclas. Las alternativas incluyenTipoEntradaTexto.teléfono, Texto
InputType.emailAdress, TextInputType.datetime,y otros. Para obtener una lista
autorizada deTipo de entrada de textoopciones, visitahttps://api.flutter.dev/flutter/
services/TextInputType-class.html.

CAPÍTULO 7Interactuando con el Usuario 225


Este consejo se aplica mientras desarrolla y prueba su aplicación. El emulador de Android y el
simulador de iPhone tienen opciones para suprimir la apariencia del teclado en pantalla, lo que
permite la entrada solo con el teclado de su computadora de desarrollo. Si esa opción está
activada, no verá el efecto de laTextInputType.númeroparámetro. Si escribe una letra en el
teclado de su computadora, esa letra aparece en el campo de texto de su aplicación.

Si planea ejecutar su aplicación en un teléfono físico real, debe probar la aplicación con el
teclado en pantalla del dispositivo virtual habilitado. Cuando lo haga, es posible que vea
algunos efectos problemáticos que no esperaba. Por ejemplo, cuando pasa de un campo
de texto a otro tipo de control, el teclado virtual no desaparece. Para hacer que el teclado
virtual desaparezca automáticamente, encierre el andamio en un detector de gestos. Así
es como lo haces:

Compilación del widget (contexto BuildContext) {

devolver GestureDetector(
en el toque: () {

actual actualFocus = FocusScope.of (contexto);


if (!currentFocus.hasPrimaryFocus) {
actualFocus.unfocus();
}
},
niño: Andamio(
// ...Etc.

Para más charla sobre eldetector de gestos,véase el Capítulo 9.

Llamada 4
En la figura 7-7, la expresión _nombreFieldController.textrepresenta los caracteres que
aparecen en el campo de texto Nombre y _incomeFieldController.text
representa los caracteres en el campo de texto Ingresos. Si el código incluye la
declaración

_nameFieldController.text = "Puedo I. Havanother";

la ejecución de esa declaración cambiaría lo que ya estaba en el campo de texto Nombre


aPuedo I. Havanother.

En la figura 7-7, la expresión _nombreFieldController.textagrega el nombre del usuario


al mensaje saliente. La expresion _incomeFieldController.textrepresenta cualquier
carácter que el usuario haya ingresado en el campo Ingresos de la aplicación, pero esos

226 PARTE 3Detalles, Detalles


los personajes vienen con una ligera captura. El material en un campo de texto es siempre unCuerda
valor, nunca un valor numérico. En la Figura 7-8, Pat entra61937en el texto de Ingresos
campo, por lo que el valor de _incomeFieldController.textes "61937" (laCuerda),no
61937 (el número).

Por suerte, DartEn tla clase tiene unanalizar gramaticalmentemétodo. Si el valor de _campo de ingresos
Controlador.textoes "61937" (laCuerda),El valor deint.parse(_ingresos
FieldController.text)es61937 (laEn tvalor numérico). En la Figura 7-7, el código

int.parse(_ingresosFieldController.texto) >= 1000000

compara un número como61937al número de alta demanda de Irving de1000000.El


resultado de la comparación esverdaderoofalso,entonces el valor de _ricoUsuario
se convierte en cualquieraverdaderoofalso.

¿QUÉ HACE UN DOT DARN?


En la programación orientada a objetos, un objeto puede tener ciertas cosas llamadaspropiedades. Usando la notación

de puntos, puede hacer referencia a cada una de esas propiedades.

Aquí están algunos ejemplos:

• CadaCuerdala instancia tienelongitudyesta vaciopropiedades.

El valor de "Dardo".longitudes4,y el valor de "".esta vacioesverdadero.

• CadaEn tel valor tieneincluso,es negativo, ybitLongitudpropiedades.

El valor de44.es paresverdadero,y el valor de99.esNegativoesfalso.El valor de


99.bitLongitudes7porque la representación binaria de 99 es 1100011, que
tiene 7 bits.

• CadaControlador de edición de textoinstancia tiene untextopropiedad.

En la Figura 7-7, el valor de _nombreFieldController.textes cualquier cadena de caracteres que


aparece en el campo de texto Nombre.

Puede aplicar la notación de puntos a expresiones de todo tipo. Por ejemplo, el valor de
(29 + 10).esParesfalso.Confrase = "Me gusta Dardo",El valor defrase. longitudes
11

(continuado)

CAPÍTULO 7Interactuando con el Usuario 227


(continuado)

Las propiedades son ejemplos de cosas llamadasmiembros. Los miembros de una clase también incluyen las

variables y los métodos de la clase. Considera lo siguiente:

• CadaCuerdainstancia tiene métodos llamadosaMayúsculas,termina con,separar,


recortar, y muchos otros.

El valor de "¡Atención!".toUpperCase()es "¡ATENCIÓN!".

El valor de "¡Santo cielo! ".recortar()es "¡Santo cielo!".

• CadaEn tel valor tiene métodos nombradosabdominales,aRadixString, y varios otros.

El valor de (-182).abdominales()es 182 porque 182 es el valor absoluto de –182. El


valor de99.toRadixString(2)es 1100011 porque la representación binaria (base 2) de 99
es 1100011.

No hay nada misterioso en los miembros de una clase. Aquí hay una clase llamada
Cuentay unprincipalfunción que llama alCuentaconstructor de la clase:

cuenta de clase {
// Dos variables miembro:
Cadena nombre del cliente;
saldo int;

// Un método miembro: depósito


anulado ({cantidad int}) {
saldo += monto;
}
}

vacío principal() {
// Una llamada al constructor de la clase Cuenta:
Cuenta miCuenta = Cuenta();

// Referencias a los miembros de la clase Cuenta:


miCuenta.customerName = "Barry Burd";
miCuenta.saldo = 100;
miCuenta.deposito(cantidad: 20);
print(miCuenta.nombreCliente);
print(miCuenta.saldo);
}

/*
* Producción:

*Barry Burd
* 120
*/

228 PARTE 3Detalles, Detalles


Las clases en las listas de Flutter de este libro también tienen miembros. Por ejemplo, la clase de la
Figura 7-7 tiene varios miembros, incluidos _controlador de campo de nombre, _ingreso de campo
Controlador, _messageToUser, initState,yconstruir.

Algunas clases tienen cosas llamadasmiembros estáticos. Un miembro estático pertenece a una clase completa,

no a ninguna de las instancias de la clase. por ejemplo, elEn tla clase tiene un método estático llamadoanalizar

gramaticalmente.Porque elanalizar gramaticalmentemétodo es estático, pones el nombre de la clase (la


palabraEn t)antes del punto. Túnoponer cualquier particularEn tvalor antes del punto. Aquí hay unos ejemplos:

• El valor deint.parse("1951")es el numero1951.

• Expresiones como1951.parse("1951"),1951.analizar()y1951.analizarson
inválido.

Ninguno de estos funciona porque, en cada caso, el valor antes del punto no es el nombre
de la clase.En t.En cambio, el valor antes del punto es un objeto, una instancia delEn t
clase.

• Poner cualquier expresión con unEn tvalor antes.analizar gramaticalmentees inválido.

Por ejemplo, el siguiente código rompe su programa:

int numeroDePayasos;
int otroNumero = numeroDePayasos.parse("2020");

Crear un miembro estático no es gran cosa. Simplemente agregue la palabraestáticoa su declaración


de miembro, así:

clase Automóvil {
estáticoint numeroDeRuedas = 4;
}

vacío principal() {
Carcaza de automóvil = Automóvil(); //
print(jalopy.numberOfWheels); Esto es incorrecto.
print(Automovil.numeroDeRuedas);
}

/*
* Producción:

*4
*/

CAPÍTULO 7Interactuando con el Usuario 229


La figura 7-7 llama a Dartint.parsemétodo: ¡un método realmente útil! Pero Dart tiene un
método aún mejor. Se llamaint.tryParse.es muy parecidoanálisis int.,pero es
más seguro de usar. Cuando usted llamaint.tryParse('Esto no es un número'),la aplicación
no te explota en la cara.

Llamada 5
Gran parte del alboroto en párrafos anteriores sobre elinitStatemétodo se aplica
igualmente a Flutterdisponermétodo. Antes deEstadoclase obtiene el heave-ho, el marco
Flutter llama al códigodisponermétodo. En la Figura 7-7, eldisponer
método hace estas tres cosas:

»llama aldisponermétodo perteneciente a la_nombreFieldController.


losdisponermétodo para _nombreFieldControllerdestruye ese controlador,
liberando cualquier recurso que el controlador esté acaparando.

»llama aldisponermétodo perteneciente a la_controlador de campo de ingresos.


Adiós, _controlador de campo de ingresos.Me alegra ver que sus recursos se
están liberando.

»llama alEstadode clasedisponermétodo.


losEstadode clasedisponerEl método, integrado sólidamente en el marco Flutter, limpia
cualquier otra cosa que tenga que ver con _Estado de mi página de inicio.como es con
estado inicial,tu propio códigodisponerel método debe llamarsuper.dispose().

Creación de botones de opción

Cada aplicación de citas tiene una pregunta sobre el género del usuario. Para esta pregunta, Doris
elige un grupo de botones de radio. El Listado 7-5 tiene mucho del código del botón de radio de Doris.

Para el resto de la aplicación de práctica de Doris con botones de radio, consulte App0705 en los
archivos que descargue del sitio web de este libro:

www.allmycode.com/Flutter

230 PARTE 3Detalles, Detalles


LISTADO 7-5: ¿Cómo te identificas?

// Este no es un programa completo. //


Algunos árboles han sido salvados. // Los
árboles están felices por eso.

enum Género { Mujer, Hombre, Otro }

String shorten(Género género) => género.toString().replaceAll("Género.", "");

class _MyHomePageState extiende State<MyHomePage> {


Cadena _messageToUser = "";
Género _géneroRadioValue;

// Y después ...

Widget _buildGenderRadio() {
fila de retorno (
niños: <Widget>[
Texto(acortar(Género.Femenino)),
Radio(
valor: Sexo.Mujer,
groupValue: _genderRadioValue,
onChanged: _updateGenderRadio,
),
SizedBox (ancho: 25.0),
Texto(acortar(Género.Masculino)),
Radio(
valor: Género.Masculino,
groupValue: _genderRadioValue,
onChanged: _updateGenderRadio,
),
SizedBox (ancho: 25.0),
Texto(acortar(Género.Otro)), Radio(

valor: Género.Otro,
groupValue: _genderRadioValue,
onChanged: _updateGenderRadio,
),
],
);
}

Widget _buildResultArea() {
fila de retorno (
(continuado)

CAPÍTULO 7Interactuando con el Usuario 231


LISTADO 7-5: (continuado)

niños: <Widget>[
Botón elevado (
niño: Texto ("Enviar"),
onPressed: _genderRadioValue != null ? _updateResults: nulo,
),
SizedBox(
ancho: 15.0,
),
Texto(
_mensaje al usuario,
textAlign: TextAlign.center,
),
],
);
}

/// Acciones

void _updateGenderRadio(Gender newValue) {


establecerEstado(() {

_géneroRadioValor = nuevoValor; });

void _updateResults() {
establecerEstado(() {

_mensaje al usuario =
"Usted seleccionó ${shorten(_genderRadioValue)}.";
});
}
}

Las Figuras 7-10 y 7-11 muestran instantáneas de una ejecución del código del Listado 7-5.

FIGURA 7-10:
Antes de seleccionar un
boton de radio.

232 PARTE 3Detalles, Detalles


FIGURA 7-11:
Después de seleccionar un

botón de opción y
presionando Enviar.

Crear una enumeración


El capítulo 3 presenta la función integrada de Flutter.Brilloenum con sus valores
Brillo.luzyBrillo.oscuro.Eso está bien, pero ¿por qué dejar que los creadores de Flutter se
diviertan? Puede definir su propia enumeración haciendo lo que ve en el Listado 7-5.

enum Género { Mujer, Hombre, Otro }

Con esta declaración, su código tiene tres nuevos valores; a saber,Género.Femenino,


Género.Masculino,yGénero.Otro.Puede usar estos valores en el resto del código de la
aplicación.

Construyendo el grupo de radio


El código del Listado 7-5 tiene tres botones de radio. Cada botón de opción tiene su propio
valorpero, en conjunto, los tres botones tienen solo unovalor de grupo.De hecho, lo
comúnvalor de grupoes lo que une los tres botones. Cuando un usuario selecciona el
botón con valorGénero femenino,lavalor de grupode los tres se convierte
Género femenino.Es como si parte del código de repente se viera así:

// No intentes esto en casa. Este es un código falso.


Radio(
valor:Género femenino,
valor de grupo:Género femenino,

),
Radio(
valor: Género.Masculino,

valor de grupo:Género femenino,

),
Radio(
valor: Género.Otro,
valor de grupo:Género femenino,

),

CAPÍTULO 7Interactuando con el Usuario 233


Cada botón de opción tiene su propioenCambiadoparámetro. En el Listado 7-5, la función que
manejaenCambiadoeventos (el _actualizaciónGenderRadiofunción) hace exactamente lo que
cabría esperar: cambia los botones de opciónvalor de grupoa cualquier valor que el usuario
haya seleccionado.

¿POR QUÉ MOLESTARSE?

Un lector de Minnesota pregunta: "¿De qué sirve elenumeracióndeclaración en el Listado 7-5? ¿Por qué
no puedo asignar elCuerdavalores "Mujer hombre",y "Otro"directamente a los tres botones de radio?

¡Buena pregunta, lector! Gracias por preguntar. La respuesta es, “Tienes razón. Puedes asignar
Cuerdavalores a los botones de radio.” Realmente no necesita una enumeración para crear un grupo de
botones de opción. El siguiente código sin enumeración es válido:

Cuerda_géneroRadioValor;

// Y después ...

Radio(
valor: "Mujer",
groupValue: _genderRadioValue,
onChanged: _updateGenderRadio,
),
Radio(
valor: "Masculino",

groupValue: _genderRadioValue,
onChanged: _updateGenderRadio,
),
Radio(
valor: "Otro",
groupValue: _genderRadioValue,
onChanged: _updateGenderRadio,
),

// Y después ...

void _updateGenderRadio(Cuerdanuevo valor) {


establecerEstado(() {

_géneroRadioValor = nuevoValor; });

void _updateResults() {
establecerEstado(() {

234 PARTE 3Detalles, Detalles


_messageToUser = "Usted seleccionópsgéneroRadioValor."; });

Entonces, en el Listado 7-5, ¿por qué me molesto en crear elGéneroenumerar? Y la respuesta es que los
géneros no son cadenas. Ser hombre no significa que una persona lleve consigo las cuatro letrasmetro,
despuésa, despuésyo, y entoncesmi. En cambio, la masculinidad es una de dos o más posibilidades,
siendo otra posibilidad la feminidad. La mejor manera de representar los géneros en el código es
enumerar las alternativas, no usar unas pocas cadenas y esperar que nadie las escriba mal.

Considere este código que usa elCuerdaescribe:

String _genderRadioValue = "Femail";

El código es incorrecto pero, en lo que se refiere al lenguaje Dart, el código es excelente.

Ahora, considere este código que usa unenumeraciónescribe:

enum Género { Femenino, Masculino, Otro }


Género _genderRadioValue = Género.Femail;

El código es incorrecto y Dart se niega a aceptarlo. Con la declaración de laGénero


enum, el programador garantiza que los únicos valores posibles de _géneroRadio
ValorsonGénero.Femenino, Género.Masculino,yGénero.Otro.Ese es un buen programa-
práctica de ming. ¡Seguridad primero!

Mostrar la elección del usuario


losacortarEl método del Listado 7-5 es una solución para una característica del lenguaje Dart
ligeramente molesta. En Dart, cada valor de enumeración tiene unEncadenarmétodo que, en
teoría, le brinda una forma útil de mostrar el valor. El problema es que cuando aplicas el
Encadenarmétodo, el resultado es siempre un nombre detallado. Por ejemplo,
Género.Mujer.toString()es "Género femenino",y eso no es exactamente lo que tu
quiere mostrar. En la Figura 7-10, el usuario ve la oraciónUsted seleccionó Mujer en lugar
de la oración demasiado técnicaSeleccionó Sexo.Femeninofrase.

Aplicando elreplaceAll("Género.", "")turnos de llamada de método "Género."en el


cadena vacía, entonces "Género femenino"se vuelve simplemente viejo "Femenino".
¡Problema resuelto! - o tal vez no.

CAPÍTULO 7Interactuando con el Usuario 235


Mira la declaración de _géneroRadioValoren el Listado 7-5:

Género _géneroRadioValue;

Esta declaración no asigna nada a _valor de radio de género,Y _género RadioValor


empieza siendonulo.Eso es bueno porque tenernulopor _género RadioValorsignifica
que ninguno de los botones del grupo de radio está marcado. Eso es exactamente lo que
desea cuando la aplicación comienza a ejecutarse.

Pero, ¿qué sucede si el usuario presiona Enviar sin seleccionar uno de los botones de opción?
Después _géneroRadioValores todavíanulo,entonces elacortarmétodosgéneroparame-
ter esnulo.Dentro deacortarmétodo, tienes la siguiente situación desagradable:

Acortar cadena (Géneronulo) =>nulo.toString().replaceAll("Género.", "");

¡Ups! cuando aplicasEncadenar()anulo,usted obtiene "nulo" (la cadena consta de cuatro


caracteres). Si no hace nada al respecto, el mensaje en la pantalla del usuario se vuelve
Seleccionó nulo. ¡Eso no es fácil de usar!

"nulo"es una palabra de cuatro letras.

En el Listado 7-5, el botón EnviaronPressedel manejador responde apropiadamente cada


vez que _géneroRadioValoresnulo.Aquí está el código:

Botón elevado (
niño: Texto ("Enviar"),
onPressed: _genderRadioValue != null ? _updateResults: nulo,
),

Si _géneroRadioValorno esnulo,el manejador es un _ agradable y convencional


Actualizar resultadosmétodo — un método que crea un _mensaje al usuarioy muestra
ese mensaje dentro de unTextoartilugio. Nada especial allí.

Pero cuando _géneroRadioValoresnulo,el botón EnviaronPressedel manejador tambiénnulo.


Y la buena noticia es que unBotón elevadocon unnuloel controlador está completamente
deshabilitado. El usuario puede ver el botón, pero la superficie del botón está atenuada y
presionar el botón no tiene ningún efecto. ¡Eso es genial! Si no se selecciona ningún género y el
usuario intenta presionar el botón Enviar, no sucede nada y no aparece ningún mensaje.

236 PARTE 3Detalles, Detalles


QUÉ HACER ?. Y ?? ¿HACER?
En el Listado 7-5, el botón Enviar no tiene vida hasta que el usuario selecciona un género. ¿Hay otras
formas de abordar el problema de la no selección todavía? Esta barra lateral explora dos de las
alternativas. Primero, aquí hay una alternativa aburrida:

Botón elevado (
niño: Texto ("Enviar"),
onPressed: _updateResults,
),

// Y luego en el código...

void _updateResults() {
establecerEstado(() {

if (_géneroRadioValor != nulo) {
_messageToUser = "Usted seleccionó ${shorten(_genderRadioValue)}."; } más
{
_messageToUser = "Todavía no has seleccionado nada.";
}
});
}

En esta versión del código, el botón Enviar siempre está habilitado y el método que
maneja un clic de botón (el _actualizarResultadosmétodo) trata _géneroRadioValor
siendonulocomo un caso especial. (Vea la primera figura de esta barra lateral).sideclaración dentro
del _actualizarResultadosEl método ciertamente funciona pero, como digo, es aburrido.

Lo que no es aburrido son los operadores nulos conscientes de Dart. Aquí hay algo de código:

String shorten(Gender gender) => gender?.toString()?.


replaceAll("Género.", "");
_mensaje al usuario =
"Usted seleccionó ${shorten(_genderRadioValue) ?? 'nada todavía'}.";

(continuado)

CAPÍTULO 7Interactuando con el Usuario 237


(continuado)

Aoperador consciente de nuloses una cosita que hace algo especial cuando la aplicas a unnulo
valor. Tomemos, por ejemplo, Dart's?. operador.

• Sigéneroesnulo, despuésgénero.toString()es "nulo".

Eso es lo que pasa cuando no usas Dart's?. operador. En Dart, todo tiene lo suyo.
Encadenarmétodo. ¿Qué mejor representación de cadenas para elnulovalor que
la cadena "nulo"?

• Sigéneroesnulo, despuésgénero?.Encadenar()esnulo.

Eso es lo que pasa cuando usas Dart's?. operador. Cuando seaalgún valores
nulo,algún valor?.algo másesnulo.Esa es la regla.

La segunda figura de esta barra lateral ilustra el comportamiento delacortarmétodo con


y sin ?. operadores.

238 PARTE 3Detalles, Detalles


¿Qué puedes hacer cuando todo elacortarel método devuelve¿nulo?Otro de los operadores de
reconocimiento nulo de Dart: el ?? operador: puede encargarse de lanulovalor. La expresion

acortar (_genderRadioValue) ?? 'nada aún'

significa cualquieraacortar(_géneroValorRadio)o 'nada aún'.ser un poco mas


preciso,

• Los ?? expresión significaacortar(_géneroValorRadio)a no ser que


acortar(_géneroValorRadio)esnulo.

• Siacortar(_géneroValorRadio)esnulo,la ?? expresión significa 'nada aún'.

Los ?? El operador tiene un nombre. se llama elsi-nulooperador. Cuando usa el operador if-null en
combinación con ?. operadores, obtendrá un resultado como el de la primera figura de esta barra lateral.

Creación de un botón desplegable

Tan pronto como se corre la voz sobre la aplicación de citas de Doris, todo el mundo quiere una
parte de la acción. La amiga de Doris, Hilda, quiere un botón desplegable para medir el nivel de
compromiso de la pareja potencial. Hilda quiere una relación comprometida y posiblemente
matrimonio. El Listado 7-6 muestra parte del código que Doris escribe para Hilda. Las figuras
7-12, 7-13 y 7-14 muestran el código en acción.*

FIGURA 7-12:
El usuario no tiene
decidido todavía.

* Gracias a David Nesterov–Rappoport, por crear las imágenes Heart y Broken Heart que
se muestran en las Figuras 7-13 y 7-14.

CAPÍTULO 7Interactuando con el Usuario 239


FIGURA 7-13:
un usuario con
pies fríos.

FIGURA 7-14:
Un usuario serio.

LISTADO 7-6: ¿Qué estás buscando?

//A este listado le faltan algunas partes.Consulte App0705 en los archivos que //
descargue del sitio web de este libro (www.allmycode.com/Flutter).

relación enum {
Amigo,
Una fecha,
En curso,
Comprometido,

Matrimonio,
}

Mapa<Relación, Cadena> mostrar = {


Relación.Amigo: "Amigo", Relación.Una fecha: "Una cita",
Relación.En curso: "Relación en curso",
Relación.Comprometida: "Relación comprometida",
Relación.Matrimonio: "Matrimonio",

};

240 PARTE 3Detalles, Detalles


List<DropdownMenuItem<Relación>> _relationshipsList = [
Elemento de menú desplegable (

valor: Relación.Amigo,
hijo: Texto(mostrar[Relación.Amigo]),
),
Elemento de menú desplegable (

valor: Relación.OneDate,
hijo: Texto(mostrar[Relación.UnaFecha]),
),
Elemento de menú desplegable (

valor: Relación.En curso,


niño: Texto (mostrar [Relación. En curso]),
),
Elemento de menú desplegable (

valor: Relación.Comprometido,
niño: Texto (mostrar [Relación. Comprometida]),
),
Elemento de menú desplegable (

valor: Relación.Matrimonio,
hijo: Texto(mostrar[Relación.Matrimonio]),
),
];

class _MyHomePageState extiende State<MyHomePage> {


Relación _relationshipDropdownValue;

// Y luego en el programa...

/// Construir

Widget _buildDropdownButtonRow() {
fila de retorno (
mainAxisAlignment: MainAxisAlignment.start, children:
<Widget>[
DropdownButton<Relación>(
elementos: _relationshipsList, onChanged:
_updateRelationshipDropdown, valor:
_relationshipDropdownValue, sugerencia: Texto
("Seleccione uno"),
),
si (_relaciónDropdownValue! = nulo)
botón plano (
niño: Texto(
"Reiniciar",
estilo: TextStyle (color: Colors.blue),
),
(continuado)

CAPÍTULO 7Interactuando con el Usuario 241


LISTADO 7-6: (continuado)

onPressed: _reset,
),
],
);
}

Widget _buildResultsImage() {
si (_relationshipDropdownValue! = nulo) {
volver Image.asset((_relationshipDropdownValue.index >= 3)
? "Corazón.png"
: "Corazón Roto.png");
} más {
devuelve SizedBox();
}
}

/// Acciones

vacío _reset() {
establecerEstado(() {

_relationshipDropdownValue = nulo; });

void _updateRelationshipDropdown(Relación nuevoValor) {


establecerEstado(() {

_relationshipDropdownValue = nuevoValor; });

}
}

Construyendo el botón desplegable


ABotón desplegableconstructor tiene varios parámetros, uno de los cuales es una lista de
elementos.Cada elemento es una instancia de laDropdownMenuItemclase. Cada una de estas instancias
tiene unvalory unniño. (Consulte la Figura 7-15.)

»un artículovalores algo que identifica ese artículo en particular.


En el Listado 7-6, los valores de los artículos sonRelación.Amigo, Relación. Una
fecha,y así. Todos son miembros de laRelaciónenumeración No quieres cosas
comoRelación.OneDateque aparece en la superficie de un elemento de menú, por
lo que . . .

»un artículoniñoes lo que se muestra en ese artículo.

242 PARTE 3Detalles, Detalles


FIGURA 7-15:
Anatomía de un
desplegable
botón.

En el Listado 7-6, los hijos de los elementos son todosTextowidgets, pero puede mostrar todo tipo de

cosas en los elementos desplegables. Por ejemplo, el hijo de un elemento puede ser un
Filaque contiene unTextowidget y unIconoartilugio.

Además de su lista deelementos,aBotón desplegableel constructor tieneenCambiado, valor,


yinsinuaciónparámetros

»losenCambiadoparámetro hace lo que tales parámetros hacen en tantos


otros constructores.

El parámetro se refiere a una función que maneja los toques, presiones, ajustes y
toques del usuario.

»En cualquier momento, elvalorel parámetro se refiere a cualquier menú desplegable


se selecciona el elemento del botón.

»losinsinuaciónEl parámetro le dice a Flutter qué mostrar cuando ninguno de los


Se han seleccionado los elementos del botón desplegable.

En el ejemplo de esta sección, Flutter muestra las palabrasSeleccione uno.

Un botón desplegableinsinuaciónnormalmente se muestra antes de que el usuario haya elegido

cualquiera de los elementos del botón. Pero el Listado 7-6 tiene un botón Restablecer. Cuando el usuario

presiona el botón Restablecer, el botónonPressedconjuntos de controladores _relaciónDrop downValue

de regresonulo,por lo que vuelve a aparecer la sugerencia del botón desplegable.

CAPÍTULO 7Interactuando con el Usuario 243


El pequeño botón de reinicio
El botón Restablecer del Listado 7-6 es interesante por más de una razón. Primero,
no es unBotón elevado.En cambio, es unBotón Plano.Abotón planoes como un
Botón elevadoexcepto . . . bueno, unbotón planoes plano. (Consulte las Figuras 7-13 y 7-14.)

Otra razón para revolcarse en el código del botón Restablecer es debido a una característica
peculiar del lenguaje Dart, una que está disponible solo desde Dart 2.3 en adelante. Aquí hay
una versión abreviada de _buildDropdownButtonRowcódigo del método en el Listado 7-6:

Widget _buildDropdownButtonRow() {
fila de retorno (

niños: <Widget>[
DropdownButton<Relación>(

),

si (_relaciónDropdownValue! = nulo)
botón plano (

),
],
);
}

En este código, elFilawidgetsniñosparámetro es una lista, y la lista consta de dos


elementos: unBotón desplegabley algo que parece unsideclaración. Pero las apariencias
pueden engañar. La cosa en el Listado 7-6 no es unsideclaración. La cosa en el Listado 7-6
es uncolección si. En el Capítulo 4, introduzco sin contemplaciones la palabrarecopilación
para describir Dartlista, conjunto,yMapatipos Una colecciónsile ayuda a definir una
instancia de uno de esos tipos.

En el Listado 7-6, el significado de la colecciónsies exactamente lo que supondrías.


Si _relaciónDropdownValueno esnulo,la lista incluye unBotón Plano.
De lo contrario, la lista no incluye unBotón Plano.Eso tiene sentido porque, cuando
_relationshipDropdownValueesnulo,no tiene sentido ofrecer al usuario una opción para
que seanulo.

Además de su colecciónsi,el lenguaje de programación Dart tiene una colección


por.Puedes leer sobre la colección.poren el Capítulo 8.

244 PARTE 3Detalles, Detalles


hacer un mapa
El Capítulo 4 presenta los tipos de Dart, uno de los cuales es elMapaescribe. AMapase parece mucho a
un diccionario. Para encontrar la definición de una palabra, busca la palabra en un diccionario. Para
encontrar una representación fácil de usar del valor de enumeraciónRelación.OneDate,
miras hacia arribaRelación.OneDateen elmostrarmapa.

Relación.Amigo/ri-lay-shuhn-barco amigo/norte.Amigo.

Relación.OneDate/ri-lay-shuhn-ship wUHn dAYt/norte.Una fecha.

Relación.En curso/ri-lAY-shuhn-barco AWn-goh-ing/adj.Relación en curso.

Relación.Comprometido/ri-lAY-shuhn-barco kuh-mIt-uhd /adj.Relacion


comprometida.

Relación.Matrimonio/ri-lAY-shuhn-barco mAIR-ij /norte.Matrimonio.

Para ser un poco más precisos, unMapaes un grupo de pares, cada par consta de una clave y
un valor. En el Listado 7-6, la variablemostrarse refiere a un mapa cuyas claves son
Relación.Amigo, Relación.Una cita,y así. Los valores del mapa son
"Amigo", "Una cita", "Relación en curso",y así. Consulte la Tabla 7-1.

TABLA 7-1 losmostrarMapa

Llave Valor Índice

Relación.Amigo "Amigo" 0

Relación.OneDate "Una fecha" 1

Relación.En curso "Relación continua" 2

Relación.Comprometido "Relacion comprometida" 3

Relación.Matrimonio "Matrimonio" 4

En un programa Dart, usa corchetes para buscar un valor en un mapa. Por ejemplo, en el
Listado 7-6, buscandoshow[Relación.UnaFecha]te da la cuerda
"Una fecha".

Además de sus claves y valores, cada entrada del mapa tiene unaíndice. El índice de una
entrada es su número de posición en la declaración del mapa, comenzando con la posición
número 0. La amiga de Doris, Hilda, quiere una relación comprometida y posiblemente
matrimonio. Entonces, el código del Listado 7-6 verifica esta condición:

Cuando esta condición es verdadera, la aplicación muestra un corazón para indicar una buena coincidencia.
De lo contrario, la aplicación muestra un corazón roto. (Lo siento, Hilda.)

CAPÍTULO 7Interactuando con el Usuario 245


Adelante y hacia arriba
El trabajo de Doris en la aplicación de citas ha valido la pena. Doris ahora está en una relación
comprometida con un desarrollador de Flutter igualmente geek, uno que tiene más de 18 años
y que gana el dinero suficiente para vivir cómodamente. Doris y su compañero vivirán felices
para siempre, o al menos hasta que Google cambie la especificación del lenguaje Dart y rompa
parte del código de Doris.

El siguiente capítulo trata sobre la navegación. ¿Cómo puede tu aplicación pasar de una página a otra?
Cuando el usuario termina de usar la nueva página, ¿cómo puede regresar su aplicación? Con más de una
página en su aplicación, ¿cómo pueden las páginas compartir información? Para obtener las respuestas a
estas preguntas, ¡simplemente diríjase a la siguiente página de este libro!

246 PARTE 3Detalles, Detalles


EN ESTE CAPÍTULO

»Pasar de una página a otra

»Visualización de una lista de artículos similares

»Tomando información de la Web

Capítulo8
Navegación, listas y
Otras golosinas

yo Al investigar este capítulo, aprendí algunas cosas interesantes sobre el arte de pasar
las páginas:

»La web tiene muchos sitios para ayudar a los músicos a resolver sus problemas de cambio de página.
Algunos sitios ofrecen consejos sobre las mejores formas de pasar las páginas manualmente. Otros

ofrecen soluciones mecánicas con pedales para controlar dispositivos de volteo. Los artículos académicos

examinan las alternativas y sacan conclusiones basadas en los estudios.

»Para los que no son músicos, varios sitios describen el cambio de página construido por uno mismo.
artilugios Ninguno de estos dispositivos mejora el aspecto hogareño de la sala de estar o el
estudio de una persona.

»Un sitio describe, en diez pasos, cómo una persona puede usar sus manos para girar el
página de un libro. El sitio tiene ilustraciones e instrucciones detalladas para cada uno de
los diez pasos. (Pensé que ya sabía cómo pasar las páginas, ¡pero tal vez me equivoqué!)

»En un foro, vi un enlace a un sitio que vende el cambio de página más profesional.
dispositivo. Cuando hice clic en el enlace, una oración continua anunciaba: "Lo sentimos, no se
pudo encontrar la página solicitada". Tal vez lo que querían decir era: "No se pudo encontrar el
dispositivo de cambio de página solicitado".

CAPÍTULO 8Navegación, listas y otras ventajas 247


»La clase Navigator de Flutter puede hacer la transición dentro de una aplicación de una página a
otro. De hecho, las funciones de navegación de Flutter son tan importantes que un
libro tiene un capítulo completo dedicado al tema. el nombre del libro esAleteo
para tontos, y el capítulo de ese libro es el Capítulo 8.

Ampliación de una clase de dardo

Si no tengo cuidado, las listas de códigos de este libro pueden volverse insoportablemente
largas. Un ejemplo simple para ilustrar un nuevo concepto puede consumir varias páginas.
Necesitarías poderes mágicos para encontrar el código nuevo e interesante de cada listado.
Para combatir esta dificultad, divido los ejemplos de algunas secciones en dos archivos: un
archivo que contiene el código repetitivo y otro archivo que contiene las nuevas funciones de la
sección. Cuando paso de una sección a la siguiente, reutilizo el archivo que contiene el código
repetitivo e introduzco un archivo separado que contiene solo las nuevas funciones.

Todo va bien hasta que trato de dividir el código de una clase en particular entre dos archivos.
Imagina que tengo dos archivos. El nombre de un archivo esReuseMe.dardo:

// Esto es ReuseMe.dart

importar 'MásCode.dart';

clase Reutilízame {
int x = 229;
}

main() => ReuseMe().displayNicely();

El nombre del otro archivo esMásCode.dart.

// Esta es una mala versión de MoreCode.dart

importar 'ReuseMe.dart';

void mostrarMuy bien() {


print('El valor de x es $x.');
}

¿Qué podría salir mal?

248 PARTE 3Detalles, Detalles


Esto es lo que sale mal: la declaración demostrar muy bienno está dentro de la
Reutilízameclase. En este par de archivos,mostrar muy bienes una función solitaria que se encuentra
fuera de cualquier clase en particular. Esto provoca dos problemas:

»La líneaReutilízame().Muestra bien()no tiene sentido.


»losmostrar muy bienfunción no puede referirse casualmente a laReutilízamede claseX
variable.

Este código es falso. ¡Tirarlo!

¡Pero espera! Un truco furtivo puede rescatar este ejemplo. Desde la versión 2.7 de Dart, puedo
agregar métodos a una clase sin ponerlos dentro del código de la clase. Indico este negocio
usando Dart'sextensiónpalabra clave. Así es como lo hago:

// Esta es una buena versión de MoreCode.dart

importar 'ReuseMe.dart';

extensión MyExtension en ReuseMe {


void mostrarMuy bien() {
print('El valor de x es $x.');
}
}

Después de hacer este cambio, elmostrar muy bienfunción se convierte en un método perteneciente
a laReutilízameclase. Dart se comporta como si hubiera escrito el siguiente código:

// La palabra clave de extensión hace que Dart finja que escribí este código:

clase Reutilízame {
int x = 229;

void mostrarMuy bien() {


print('El valor de x es $x.');
}
}

Dentro demostrar muy biencuerpo del método, el nombreXse refiere aReutilízamede clase
Xvariable. Y cada instancia de laReutilízamela clase tiene unmostrar muy bienmétodo.
Entonces la llamadaReutilízame().Muestra bien()tiene mucho sentido.

Todo funciona. Y, lo mejor de todo, puedo cambiar elMásCode.dartarchivo para otra


versión del archivo cuando lo desee.

CAPÍTULO 8Navegación, listas y otras ventajas 249


// Otra buena versión de MoreCode.dart

importar 'ReuseMe.dart';

extensión MyExtension en ReuseMe {


void mostrarMuy bien() {
impresión(' * $x * ');
imprimir(' ** $x ** ');
imprimir(' *** $x ***');
imprimir('**** $x ****');
}
}

puedo cambiar elmostrar muy bienfunción sin tocar el archivo que contiene el original
Reutilízamedeclaración de clase. ¡Eso es útil!

Las extensiones no están disponibles en todas las versiones de Dart. Si Android Studio se
queja de su uso de extensiones, busque elambientesección de su proyectopubspec.yaml
expediente. QueambienteLa sección puede ser algo como esto:

ambiente:
SDK: ">=2.1.0 <3.0.0"

Cambie el número de versión inferior de Dart así:

ambiente:
SDK: ">=2.6.0<3.0.0"

El nombre de una extensión distingue esa extensión de cualquier otra extensión en la


misma clase. Por ejemplo, imagina que he definidoMi extensióny has definido
suextensión,tanto en elReutilízameclase:

extensión YourExtension en ReuseMe {


void mostrarMuy bien() {
imprimir('!!! $x !!!');
}
}

Con dos extensiones declarandomostrar muy bienmétodos, la expresión


Reutilízame().Muestra bien()es ambiguo. Para aclarar la confusión, nombre una de las
extensiones explícitamente:

YourExtension(ReuseMe()).displayNicely()

250 PARTE 3Detalles, Detalles


De una página a otra
Probablemente haya usado una aplicación con una interfaz maestro-detalle. Ainterfaz maestro-detalle
tiene dos paginas. La primera página muestra una lista de elementos. Cuando el usuario selecciona un
elemento de la lista, una segunda página muestra detalles sobre ese elemento. El primer ejemplo de
este capítulo (en los Listados 8-1 y 8-2) tiene una interfaz maestro-detalle simplificada. ¿Y por qué digo
"desnudo"? La lista de la página maestra consta de un solo elemento: el nombre de una película en
particular.

LISTADO 8-1: Reutilizar este código

// App08Principal.dart

importar 'paquete: flutter/material.dart';

importar 'App0802.dart'; // Cambie esta línea a App0803, App0804 y así sucesivamente.

void main() => ejecutarApp(App08Main());

clase App08Main extiende StatelessWidget {


@anular
Compilación del widget (contexto BuildContext) {
devolver MaterialApp(
inicio: MovieTitlePage(),
);
}
}

clase MovieTitlePage extiende StatefulWidget {


@anular
MovieTitlePageState createState() => MovieTitlePageState();
}

class MovieTitlePageState extiende Estado<MovieTitlePage> {


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

texto del título(


'Título de la película',

),
),
cuerpo: relleno (
relleno: const EdgeInsets.all(16.0), hijo:
Center(
(continuado)

CAPÍTULO 8Navegación, listas y otras ventajas 251


LISTADO 8-1: (continuado)

hijo: buildTitlePageCore(),
),
),
);
}
}

clase DetailPage extiende StatelessWidget {


resumen final = '(De themoviedb.com) Un día en el trabajo, sin éxito '
El titiritero Craig encuentra un portal en la cabeza del actor John Malkovich. El portal
pronto se convierte en una pasión para cualquiera que entre en su mundo loco y
controlador de apoderarse de otro "cuerpo" humano;

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

texto del título(


'Detalles',
),
),
cuerpo: relleno (
relleno: const EdgeInsets.all(16.0), hijo:
Center(
hijo: buildDetailPageCore(contexto),
),
),
);
}
}

LISTADO 8-2: Navegación básica

// App0802.dardo

importar 'paquete: flutter/material.dart';

importar 'App08Main.dart';

extensión MoreMovieTitlePage en MovieTitlePageState {


ir a la página de detalles () {

Navegador.push(
contexto,
RutaPáginaMaterial(

252 PARTE 3Detalles, Detalles


constructor: (contexto) => DetailPage(),
),
);
}

Widget buildTitlePageCore() {
columna de retorno (

crossAxisAlignment: CrossAxisAlignment.center, niños:


<Widget>[
Texto(
'Siendo John Malkovich',
textScaleFactor: 1.5,
),
SizedBox (altura: 16.0),
BotónElevado.icono(
icono: Icono(Iconos.arrow_forward),
etiqueta: Texto('Detalles'),
onPressed: ir a la página de detalles,

),
],
);
}
}

extensión MoreDetailPage en DetailPage {


Widget buildDetailPageCore(contexto) {
columna de retorno (

crossAxisAlignment: CrossAxisAlignment.center, niños:


<Widget>[
Texto(
visión general,

),
],
);
}
}

Para ejecutar la primera aplicación de este capítulo, su proyecto debe contener tanto el Listado 8-1
como el Listado 8-2. Cada uno de estos listados depende del código del otro listado. De hecho,
muchos de los listados de este capítulo dependen del código del Listado 8-1.

Los listados 8-1 y 8-2 deben estar en archivos .dardoporque ambos listados contienen
declaraciones de importación.

CAPÍTULO 8Navegación, listas y otras ventajas 253


El listado 8-2 no tiene un método principal. Entonces, para ejecutar la aplicación en los Listados
8-1 y 8-2, busca elApp08Main.dartpestaña arriba del editor de Android Studio. Hace clic
derecho en esa pestaña y luego selecciona Ejecutar 'App08Main.dart' en el menú que aparece.

Las Figuras 8-1 y 8-2 muestran las páginas generadas por el código de los Listados 8-1 y 8-2.

FIGURA 8-1:
un muy simple
página principal.

FIGURA 8-2:
un muy simple
página de detalles

La Figura 8-1 muestra la página de inicio de la aplicación: una página con unBotón elevadoen
eso. Cuando el usuario presiona este botón, Flutter llama alir a la página de detallesmétodo
del Listado 8-2. losir a la página de detallesmétodo llama alNavegadorde claseempujar
método. Los parámetros de laempujarel método apunta directamente a laDetallePágina
clase. Así que la aplicación salta a su segunda página: laDetalleClasepágina en la Figura
8-2.

La esquina superior izquierda de la figura 8-2 tiene una pequeña flecha que apunta hacia atrás. Flutter
crea esa flecha automáticamente cada vez que navega a una página que tiene una barra de
aplicaciones. Cuando el usuario presiona esa flecha, la aplicación regresa a la primera página: la
MovieTitlePage.

Un icono en un botón
Para un poco de ternura, agrego un ícono (una pequeña flecha que apunta hacia adelante) al
Botón elevadoen la Figura 8-1. Para que esto suceda, uso la palabraiconoun montón de
veces en el Listado 8-2. En lugar de llamar a lo ordinarioBotón elevadoconstructor, llamo
Flutter'sElevadoButton.iconconstructor. Entonces, para el constructoricono

254 PARTE 3Detalles, Detalles


parámetro, escriboIcono(Iconos.arrow_forward),lo que significa, "Construir un
verdaderoIconowidget cuya apariencia es la del integrado de FlutterIconos.flecha_
delanterovalor."

Flutter tiene un montón de iconos integrados. La mayoría de ellos son interfaces de usuario familiares.
iconos de la cara, comosubir volumen, advertencia,yseñal_celular_4_bar.Pero otros son
los que no esperas encontrar. Por ejemplo, Flutter tiene unmascotasicono (una imagen de un
pata), unacasinoicono (la cara de un dado), y unairline_seat_legroom_reduced
icono (una persona acurrucada en un espacio pequeño).

Empujando y haciendo estallar

He aquí alguna terminología útil:

»Una pagina que llamaNavegador.pushes unfuentepágina.


En los Listados 8-1 y 8-2, elPelículaTítuloPáginaes una página fuente.

»Una página que el usuario ve como resultado de unaNavegador.pushllamar es undestino


página.

En los Listados 8-1 y 8-2, elDetallePáginaes una página de destino.

Algunas transiciones van de una página de origen a una página de destino; otros van de una
página de destino a una página de origen. En el ejemplo de esta sección,

»El usuario presiona elBotón elevadoen la Figura 8-1 para ir de fuente a


destino.

»El usuario presiona el botón Atrás de la barra de la aplicación en la Figura 8-2 para ir del destino
ción a la fuente.

En su mayor parte, las transiciones de una aplicación móvil forman una estructura conocida comopila.
Para crear una pila, apila cada página nueva encima de todas las páginas existentes. Luego, cuando
esté listo para eliminar una página, elimine la página que está en la parte superior de la pila. Es como
un sistema de antigüedad para las páginas. La página más joven es la primera en ser eliminada. Con
esteÚltimo en entrar primero en salir(LIFO), el usuario forma una imagen mental clara de su lugar
entre las páginas de la aplicación.

Aquí hay un poco más de terminología:

»Cuando agrega algo a la parte superior de una pila, estáemprendedoren la pila.


»Cuando quitas algo de la parte superior de una pila, estáshaciendo estallarfuera de la
pila.

CAPÍTULO 8Navegación, listas y otras ventajas 255


En el Listado 8-2, el nombreNavegador.pushsugiere empujar una página sobre una pila de páginas. De
hecho, cuando pienso en las transiciones de página, siempre imagino páginas encima de páginas. La página
más reciente oscurece las páginas más antiguas que se encuentran debajo de ella. Durante una ejecución de
la primera aplicación de este capítulo, elDetallePáginase sienta cómodamente en la parte superior de la
Página de título de película,oscureciendo por completo laPelículaTítuloPáginadesde la vista del usuario.

En algunas situaciones, la idea de apilar una página encima de otra no es apropiada. Tal
vez no quiera colocar una página de destino encima de una página de origen. En su lugar,
desea reemplazar una página de origen con una página de destino. Para hacer esto en el
Listado 8-2, realiza un pequeño cambio: cambia las palabrasNavegador.pusha
las palabrasNavigator.pushReemplazo.Cuando lo haces, elPelículaTítuloPáginaaspecto
como lo hace en la Figura 8-1, pero elDetallePáginadifiere un poco de la imagen en la Figura 8-2. En el
nuevopágina de detalles,la barra de aplicaciones no tiene botón Atrás.

En Flutter, las pantallas y las páginas se llamanrutas. Es por eso que el Listado 8-2 contiene un
MaterialPageRoutellamada del constructor.

Para hacer que su aplicación se vea como una aplicación para iPhone, use los widgets Cupertino de
Flutter en lugar de los widgets de Material Design y construya unCupertinoPáginaRutamás bien que
aMaterialPageRoute.ACupertinoPáginaRutahace que las transiciones de página se vean
"Como una manzana". Para obtener más información sobre los widgets Cupertino de Flutter, consulte el Capítulo 3.

Pasar datos de origen a destino


A veces, desea pasar información de una página a otra. El siguiente ejemplo (vea el
Listado 8-3) le muestra cómo una fuente envía información a un destino.

Antes de intentar ejecutar la aplicación de esta sección, cambie uno de losimportarlíneas en el


Listado 8-1. Cambio 'App0802.dardo'a 'App0803.dardo'.Realice cambios similares para
ejecutar los Listados 8-3, 8-4, 8-5, 8-7, 8-8 y 8-10.

LISTADO 8-3: De la página de título de la película a la página de detalles

// App0803.dardo

importar 'paquete: flutter/material.dart';

importar 'App08Main.dart';

extensión MoreMovieTitlePage en MovieTitlePageState {


static bool _isFavorite = true; // Puede cambiar esto a falso.

256 PARTE 3Detalles, Detalles


ir a la página de detalles () {

Navegador.push(
contexto,
RutaPáginaMaterial(
constructor: (contexto) => DetailPage(),
configuración: configuración de ruta (
argumentos: _esFavorito,
),
),
);
}

Widget buildTitlePageCore() {
columna de retorno (

crossAxisAlignment: CrossAxisAlignment.center, niños:


<Widget>[
Texto(
'Siendo John Malkovich',
textScaleFactor: 1.5,
),
SizedBox(altura: 16.0),
RaisedButton.icon(
icono: Icono(Iconos.arrow_forward),
etiqueta: Texto('Detalles'),
onPressed: ir a la página de detalles,
),
],
);
}
}

extensión MoreDetailPage en DetailPage {


Widget buildDetailPageCore(contexto) {
columna de retorno (

crossAxisAlignment: CrossAxisAlignment.center, niños:


<Widget>[
Texto(
visión general,

),
Visibilidad(
visible: ModalRoute.of(contexto).settings.arguments ?? falso, hijo:
Icono(Iconos.favorito),
),
],
);
}
}

CAPÍTULO 8Navegación, listas y otras ventajas 257

También podría gustarte