C | Flutter básico - SergioDuarte369/flutter_docu GitHub Wiki
-Aquí veremos cómo crear un proyecto desde Android Studio, vamos a tener explicación de cómo nombrar el package, así como selección de lenguaje tanto para Android como para iOS.
[https://meedu.app/me/course/5f01e2d1a0c908191266f5d3?section=5fd4f2ead7b0e7025d16e61d&lesson=5fd4f305d7b0e7025d16e61e](Creando proyecto con Android Studio)
Flutter con Visual Studio Code
-Abrimos terminal y navegamos al directorio donde queremos crear el proyecto. -Tip. Podemos arrastar la carpeta en donde queremos crear el proyecto al terminal para facilitar y agilizar la navegación al directorio que quremos usar para la creación del proyecto.
--Usamos el comando:
flutter create "nombre del proyecto"
--Para especificar el lenguaje para Android e iOS, así como el soporte para windows 10, así como el package name o el bundleId ejecutamos el siguiente comando:
flutter create -i swift -a java --androidx --org=app.meedu hello_world
Proyecto con línea de comandos
Estructura de un proyecto Flutter
-El método main de nuestras aplicaciones en Dart, se le conoce como el entrypoint, ya que es el método necesario para poder correr nuestras aplicaciones en Flutter
-En caso de no crear el método main, sería imposible ejecutar nuestras aplicaciones en Flutter.
-Usando comandos en la temrinal para correr la aplicación en lugar de correr la aplicaciónd desde VScode consume menos recursos. Al usar VScode para correr la aplicación, así como hot reload,etc... este proceso consume más recursos
-Para poder usar los breakpoints en nuestras aplicaciones tenemos que correr nuestra aplicación en modo Debug, de lo contrario, estos breakpoints serán ignorados.
-Para instalar dependencias lo hacemos usando la página pub.dev pub.dev
-El widget Scaffold defina la forma genérica de una pantalla creada con el estilo Material. Así como tiene integradas todas las partes necesarias para crear una pantalla. AppBar, body, BottomNavigationBar. Scaffold
-Nos ayuda a evitar que el contenido quede detrás del notch o de ciertas partes que pueden hacer que no se vea el contenido correctamente o que ciertas partes del contenido no se vean entero Widget Safearea
--Usando paquetes de terceros para iconos en Flutter Iconos 2
Spacer y Expanded -Spacer nos ayuda a dar espacion entre widget, este widget dará el máximo de espacio posible entre los widgets entre los que coloquemos el Widget Spacer -Expanded es usado para que un Widget ocupe el máximo de espacio posible. El widget que usamos como child en Expanded ocupará el máximo de espacio posible.
-Cuando trabajamos con botones, podemos usar los botones de Material, de Cupertino o crear nuestros propios botones.
-Creamos nuestros Widgets reutilizables, creandolos en un archivo separado, dándoles todo lo necesario para después importalos en donde queremos usarlos y podemos pasar los parámetros que deseamos. Esto sería similar a crear componentes en React.
En flutter tenemos **StatelessWidget **y StatefullWidget
-Los StatelessWidgets son Widgets sin estado, deben tener todas las propiedades final, consumen menos recursos que los StatefullWidgets, ya que sólo se usan para mostrar elementos. Si queremos cambiar cómo se visualizan los elementos tenemos que hacerlo desde la declaración del Widget.
Ejemplos de este tipo de widgets. Text,Row, Column y Container.
-Los StatefullWidgets son widgets que tienen un estado y un ciclo de vida. El estado puede cambiar durante el ciclo de vida de un widget. El estado practicamente define el comportamiento y la apariencia del Widget. Por ejemplo la app de ejemplo de Flutter, donde tenemos un botón que cambiar el estado de la pantalla. Cuando el estado del Widget se asociado con un contexto, el estado es considerado como mounted o montado.
El contexto le dice a Flutter en qué parte del arbol de Widget se encuentra nuestro Widget, de esta forma sabe dónde renderizarlo. También nos permite conocer el padre de un Widget, saber si fue renderizado, navegar entre pantallas, mostrar diálogos o acceder a la configuración del widget principal o MyApp desde cualquier otro Widget. Cada Widget en Flutter tiene su propio contexto, ver imagen de Widget tree en Flutter.
Ejemplos Checkbox, Button.
-Dentro de StatefulWidget, no solemos usar final en las variables. Debido a que estas suelen ser mutables. -Es recomendable crear las variables usando el tipo de dato(String, int, double). Esto es mejor que usar var para crear las variables.
-Los StatefulWidget están formados por dos clases. La primera debe extender de StatefulWidget y debe sobreescribir el método createState y debe retornar una instancia de State.
La segunda clase debe extender de State y será el estado de nuestro statefulWidget. Debe sobreescribir el método build que retornará nuestro widget para construir nuestra aplicación. Dentro de esta clase podemos tener las propiedades que van a simbolizar el estado de nuestro statefulWidget. También podemos tener métodos que modifiquen el valor del estado y que llamando a setState o con otros formas volverá a renderizar nuestra vista.
--Modificando color de fondo pantalla, en funcíon de la pantalla que está activa. Ejercicio color fondo en función de pantalla activa
Trabajando con ciclo de vida y valores iniciales
-Podemos acceder a variables dentro de nuestro Widget usando widget.variable. Así podemos acceder a ciertas variables que tenemos en Statefulwidget desde nuestro state. Para acceder a widget. tengo que estar dentro de una función. No puedo acceder a widget. desde fuera de una función de alguna de las dos clases por las que está formada un StatefulWidget.
-Cada vez que modifamos algo dentro del método build se modifica la vista. Esto hace que se vuelva a renderizar la aplicación tomando los nuevos cambios.
-Trabjando con actualizaciones de vistas con condiciones sobre el Widget viejo link
-Creando un cronómetro en nuestro statefulwidget -Trabajando con formatos para el widget Duration link
-ImageNetwork imagenes de internet
-Image imagenes desde nuestro escritorio
-Cuando agregramos archivos en pubspec.yaml, por ejemplo en el caso de los assets, tenemos que detener el proyecto y volverlo a ejecutar
-image.network sólo es recomendado usarlo para imágenes pequeñas
-Al usar cached.image la primera vez tarda lo mismo que image.network, cuando cargamos las siguientes veces, es mucho más rápido ya que la imagen se encuentra ya en cache
-ESto nos ayuda a hacer scroll en la pantalla, nos ayuda a evitar errores de overflow. ESto es util por ejemplo en formularios para poder mostrar varios campos de texto.
-SingleChildScrollView debe estar dentro de una zona delimitadora. Si por ejemplo lo tenemos dentro de un column, no sabrá la zona limitadora y esto provocará un fallo de overflow, para evitar esto podemos dar una altura a nuestro column para evitar este error. También podemos envolver nuestro singleChildscrollView en un Expanded. TAmbién podemos usar un Container con una altura determinada que delimite la zona del singleChildScrollView. También podemos tenerlo dentro un SafeArea lo que mostrará toda la parte posible de la pantalla.
-SingleChildScrollView tiene diferentes propiedades que nos permiten determinar la dirección del scroll
-Anteriomente usando list.generate, nos dimos cuenta de que perjudicaba el funcionamiento de la aplicación al cargar gran cantidad de elementos. Para mejorar el rendimiento Flutter nos proveé otro Widget para mejorarlo.
-Listview nos ayuda a trabajar con listas de una forma más eficiente. Listview debe ser usado cuando voy a trabajar con gran cantidad de elementos.
-La forma más basica de un ListView es usar tan sólo ListView. Para trabajar con gran cantidad de elementos debemos usar ListView.builder, esta es la forma indicada para trabajar con gran cantidad de elementos.
-ListView.builder unicamente carga los elementos que tenemos en pantalla, los demás están en memoria y sólamente van a ser renderizados en el momento necesario. Esto nos proporciona una gran mejora en el rendimiento de nuestra aplicación.
-Dentro de ListView.builder tenemos propiedad como itemExtent que nos permiten mejorar el rendimiento del scroll, ya que nos permite dar la altura de cada elemento y esto permite ahorrar recursos al dispositivo al no tener que calcular la altura, esto sólo nos sirve cuando la altura de cada elemento es igual, en caso de tener una altura dinámica, no podemos usar esta propiedad
-Midiendo el rendimiento de listview.builder vs listView ListView builder
-Cuando tengamos un listView horizontal, tenemos que delimitar al altura que va a tener, en caso de no tenerlo vamos a tener un fallo. Si usamos Widget como SafeArea va a tomar todo el espacio que pueda. Al delimitar la altura podemos dar el tamaño que deseamos.
-Los ListView por defecto se van a mostrar por debajo de nuestro status bar, aunque no esté dentro de un SafeArea. para evitar esto, podemos modificar la propiedad padding y ponerla en 0. Aunque normalmente no se modifica esta propiedad del widget
###GridView GridView
-GridView nos sirve para crear elementos en grilla. LA forma más sencilla de usar GridView en con .count.
-Para trabajar con gran cantidad de elementos tenemos que usar GridView.builder esto al igual que con ListView nos ayuda a trabajar de forma más sencilla con gran cantidad de elementos
-Importante el vídeo del enlace para entender bien cómo funciona la navegación en Flutter Navigator push
-Cuando usamos pushReplacement, esto hace que al navegar a otra página se remplace la ruta actual por la ruta a la que vamos a navegar, esto es util en caso donde queremos evitar que el usuario vuelva atrás.
-Para volver a la ruta anterior usamos la clase Navigator.pop, esto elimina la página actual del stack y volvemos atrás
-Si usamos Navigator.canPop, esto retorna true cuando tenemos al menos dos páginas en el historial. Si no hay al menos dos páginas no volverá atrás. Esto sirve para evitar errores cuando navegamos usando pushReplacement y después forzamos a volver atrás, esto nos devolverá una pantalla negra, ya que el stack de páginas no tiene qué renderizar.
Pasar y retornar datos de una página
-Navigator.push retorna un dato de tipo Future, esto significa que podemos usar async await al hacer llamada a las rutas push para poder usar await para ejecutar el código en un cierto orden deseado.
-En Navigator.pop también podemos pasar como segundo parámetro algún dato que deseemos.
-También podemos especificar el tipo de datos que vamos a retornar. Para hacer esto tenemos especificar en Navigator.push. Para esto tendremos que especificar en todos los lugares necesarios el tipo de retorno para evitar errores.
-Por defecto en iOS el gesto de regresar a la pantalla anterior deslizando a la izquierda está deshabilitido. Es decir el parámetro onWillPop de WillPopScope está false por defecto.
-Este widget es muy util para bloquear un diálogo o dar tocar en un botón para regresar y que sólo pueda regresar después de aceptar o cancelar un formulario. Usando este widget puedo detectar cuando el usuario toca en volver atrás para poder llevar a cabo ciertas acciones antes de llegar a la pantalla anterior o mientras que navega hacia la pantalla anterior.
-Cuando usamos pushAndRemoveUntil, lo que estamos haciendo es navegar a una página y eliminando todas las páginas anteriores del stack de navegación.
-En caso que queramos que no se elimine una página en concreto de el stack de navegación no elimine esa página en concreto.
-Esto eliminará todas las páginas del historial de navegación a excepción de las que le indiquemos que no lo haga
-Aquí vamos a navegar utilizando los nombres de las rutas. Para esto tenemos que crear en nuestro main en MaterialApp, las routes para crear las rutas de navegación de nuestra aplicación. Aquí vamos a definir el nombre de la ruta y vamos a retornar una instancia de un Widget para cada nombre de la ruta, de esta manera estamos haciendo la asociación de un nombre con un Widget para la navegación en nuestra aplicación.
-Después para navegar a ciertas rutas en nuestra aplicación tenemos que usar Navigator.pushNamed(context, route); - En route vamos a pasar el nombre de la ruta como la hemos definido en main.
--Para mejorar la navegación y nuestro código es mejor crear un archivo para crear todos los nombres de nuestras rutas, así como su asociación con los Widgets que vamos a mostrar. Navigator pushNamed, archivo para rutas
-Al crear este archivo si sabemos el valor que le vamos a dar a nuestra variable de rutas, en caso de saberlo, es mejor usar const, en lugar de final, ya que esto nos ayuda a mejorar el rendimiento de nuestra aplicación. En esta caso usamos const al saber el valor de antemano, final calcula el valor en timepo de ejecución. Por el contrario const lo hace en el tiempo de compilación, esto nos ayuda a tener que calcular este datos en ejecución cuando realmente ya sabemos el valor que va a tener. La mejora del rendimiento proviene de que cuando vamos a ejecutar el código previamente se ha compilado, al usar valores const y asignar su valor en compilación ahorra realizar operaciones posteriormente en tiempo de ejecución, esto supone una mejora en el rendimiento de la aplicación.
-Creamos también un archivo dentro de la carpeta de rutas pages.dart además de routes.dart. Ambos archivos estarán formados por clases abstractas. para evitar que se puedan instanciar y van a tener el contenido necesario para la navegación en nuestra aplicación.
-El archivo routes.dart va a estar formado por static const String variable = "/nombre ruta"; Esto será creado para asignar el nombre a cada una de las rutas. Después en pages, vamos a relacionar el nombre de las rutas con el contenido que vamos a mostrar en cada una de estas.
-En pages.dart vamos a tener una clase abstracta llamada pages compuesta por un static Map routes que va a contener el nombre de cada ruta, con el Widget o contenido que va a devolver cada una de estas rutas al ser llamada.
-Después para realizar la navegación. Usaremos NAvigator.pushNamed(context,newRouteName)
-Cuando estamos usando namedRoutes en main dentro de return MaterialApp, cuando usamos namedRoutes en lugar de usar home:, es recomendable usar initialRoute y pasarle la ruta que queremos que sea la ruta inicial de nuestra aplicación.
-Esto nos ayuda a navegar de forma más sencilla, aunque debemos tener en cuenta la importancia del context para ubicar los diferentes widget, su relación con otros widgets, jerarquía,etc...
-Para navegar sin context, tenemos que crear un archivo router.dart, vamos a crearla como un singleton es una clase que sólo puede ser instanciada dentro de la misma clase, esto nos garantiza que sólo tenemos una instancia de esta a lo largo de toda nuestra aplicación. Al hacer esto siempre tenemos la misma variable cuando la llamamos en lugar de tener una nueva instancia cada vez que lo llamamos. Ver video.
###ListTile -Es un Widget muy util para trabajarlo con ListView ya que es una especie de botón que incluye un título ListTile
-En lugar de usar showDialog, usamos AlertDialog o CupertinoDialog, ya que estos Widgets vienen con propiedades para poder aplicar estilo de forma más fácil.
-El CupertinoAlertDialog, es un diálogo creado con el estilo de diseño cupertino de iOS.
-Por defecto cuando tocas fuera del diálogo no se cierra, para tener este comportamineto tenemos que modificar la propiedad barrierDismissible: false. Para que nos deje realizar ese comportamiento.
Este Widget es un tipo de alerta que nos proporciona diversas opciones para elegir. link
-Podemos usar filter: ImageFilter.blur(sigmaX:, SigmaY:), para difuminar alguna parte de nuestra diálogo, en este caso podemos difuminar las opciones del paso anterior para centrar la atención del usuario en el popup y las opciones que hemos mostrado.
-Para minimizar el keyboard después de escribir, tenemos que envolver el Widget donde esté el TextField en un GestureDetector. Después dentro del método onTap, tenemos que usar FocusScope.of(context).unFocus();
-Dentro del TextFiled, tenemos el método onChanged, aquí podemos hacer print para mostrar lo que estamos ingresando en el campo de texto en cada momento
-Para cambiar el tipo de teclado que tenemos, usamos la propiedad keyboardType: TextInputType. , aquí le asignamos el tipo de dato que queremos para nuestro teclado.
-Para desactivar el autocorrector para un campo de texto, usamos la propiedada autocorrect: false.
-Para usar un campo que sólo sea de lectura. readOnly: true.
-Para campos de texto con contraseñas. Usamos la propiedad obscureText: true.
-Para no mostrar algún carácter, usamos obscuringCharacter; "Aquí pasamos los caracteres que queremos oscurecer".
-Para fijar un máximo de líneas para nuestro campos de texto. maxLines: 3. Si aquí pasamos null, la altura del elemento se va calculando dinámicamente.
-Para cambiar el color del teclado de claro o oscuro. keyboardAppearance: Brightness.dark
-Para cambiar una tecla de enviar o pasar al siguiente textField. Usanos textInputAction: TextInputAction.next, .go, de esta forma podemos cambiar el nombre de la tecla para que se ajuste más a lo que estamos trabajando en ese textField en concreto.
-Vamos a trabajar guardando datos de los campos de texto. Tenemos que usar un StatefulWidget
-Después para asignar el valor del campo de texto a la variable de nuestro statefulWidget, en el método onChanged de nuestro Textfield, igualamos el nombre del parámetro de este método a la variable que hemos creado en nuestro statefulWidget. Por ejemplo:
_password es el nombre de la variable privada que tenemos en nuesttro statefulwidget
onChanged: (String text){
_password = text;
}
-Después en un botón, usamos la opción de onPressed para enviar una función que va a ser la encargada de mandar los datos que tenemos dentro de nuestro TextField. En un primer momento para probar podemos hacerlo con print que muestren el contenido de los campos de texto al tocar en submit.
-Para mejorar nuestros TextField, en los campos de texto usamos decoration: InputDecoration(), y usamos las propiedades de este para decorar los campos de texto. Dentro de InputDecoration podemos establecer un mensaje como label que será mostrado para ayudarnos en el campo de texto para identificar lo que tenemos que completar en este campo. Dar padding, cambiar el tipo del borde, también podemos pasar suffix-Icon para mostrar iconos al final del campo de texto, podemos mostrar un texto de ayuda con hintText.
-Para poder cambiar el estilo de una página concreta, sin tener que cambiar el estilo de toda nuestra aplicación en el main, podemos usar el Widget Theme y envolver nuestro Widget con Theme, así podremos modificar el theme de esa página en concreto.
--Aunque no es recomendable trabajar con Theme, es mejor convertir el componente en un statefulWidget y después trabajar todo el estilo y todos los aspectos que deseemos en ese componente, después lo agregaremos a nuestra aplicación.
-Dentro de TextFiled, tengo la propiedad decoration : InputDecoration. Aquí puedo pasar diferentes propriedades para modificar el estilo y el texto de ayuda que vamos a mostrar en nuestro textField. Aquí también puedo modificar el tipo de borde que vamos a usar o incluso no mostrar ningún tipo de borde.
--Cuando hemos creado nuestro componente en un archivo aparte con un StatefulWidget, ahora reemplazamos en TextField que usábamos por nuestro componente.
--Trabajamos con FocusNode para poder realizar acciones al tocar en un campo de texto o tocar fuera. También podemos ejecutar diferentes métodos de FocusNode para realizar diferentes acciones.
--Usamos el operador ternario para cambiar el color del campo de texto en función de si el FocusNode está en este campo de texto o si no está en él.
-Aquí también estamos mostrando y escondiendo la contraseña cuando tocamos el suffix icon en el TextField.
--Vamos a usar el parámetro TextEditingController para poder trabajar con datos para poder mostrarlos desde el momento que ejecuto nuestra aplicación. Flutter crea automáticamente un TextEditingController para el TextField.
--TEnemos que igualar nuestro TextEditingController al controller de nuestro TextField.
--Después en nuestro initState, tenemos que usar controller.text = y le vamos a dar el valor que queremos que muestre al cargar nuestra página.
--EStos controllers tengo que deshecharlos en dispose.
--También podemos addListener al controller, para escuchar nuestro campo de texto cada vez que hay un cambio dentro de este.
-Hemos creado que cuando el campo de texto no sea de tipo contraseña, hemos añadido un icono para borrar el texto dentro de este.
--Aquí hemos usado textEditingController y FocusNode con nuestro TextField
-Dentro de Checkbox, en onChanged tenemos que actuzalir el state cada vez que tocamos en el checkbox, para esto tenemos que hacer setState cambiando el valor de nuestro tipo de dato en el checkbox.
-Dentro de checkbox, podemos usar la propiedad materialTapTagetSize y hay podemos realizar cambios para cambiar la posición de este. También podemos envolver nuestro checkbox en un SizedBox y así podemos jugar mejor con las dimensiones y la posición que le vamos a asignar.
link - Trabajando con una lista de Checkbox
--Aquí vamos a usar map para iterar en una lista accediendo a los datos que nos interesan. Después de iterar usando map, tenemos que usar .toList()
--Map no nos permite iterar usando el index, en caso de necesitar acceder a nuestro index, tenemos que user List.generate().
--Aquí en lugar de usar Row, también podemos usar CheckBoxListTile para trabajar con una lista de respuestas checkbox para nuestra pregunta. Usando este widget podemos alinear y mejorar nuestro checkbox de forma más fácil.
--Usando controlAffinity podemos dar mejor estilo, incluso trabajar en función de la plataforma que estemos usando.
--Importante: Tanto en los checkbox, radio, y switch no podemos pasar en value datos de tipo null, ya que esto provocaría un fallo en nuestra aplicación. Para evitar esto tenemos que dar un valor a nuestras variables cuando creamos el valor de un checkbox, radio y switch.
-Aquí necesitamos una lista para pasar a nuestro parámetro items dentro de DropDownButton.
-Vamos a retornar un Widget de tipo DropDownMenuItem, para cada uno de los elementos de nuestro DropDownButton.
-Podmeos usar tanto map al mi list, como List.generate para generar la lista de los elementos de mi DropDownButton
-Esto nos va a mostar un diálogo que nos muestre un DatePicker
-Diferencia entre usar en una función y usar Future. Si necesitamos ejecutar el código después en algún otro lugar usando async, tenemos que crear la función o método como Future en caso de no tener que hacer, nos basta con usar void.
--para el value, tenemos que crear una variable o propiedad double. Para onChanged tenemos que ajustar el valor al nuevo valor, eso podemos hacerlo con setState en el onChanged.
-También podemos definir un valor mínimo y un valor máximo. Crear divisiones con la propiedad divisions. Podemos crear un label, para tener una etiqueta que al mover el slider nos proporcione el valor deseado. Con activeColor podemos modificar el color del Slider.
###Creando un diálogo de búsqueda. link
--Vamos a crear un diálogo de búsqueda usando tipos genéricos para evitar tener que crear uno distinto para cada tipo de dato.
-Vamos a crear nuestra clase con datos de tipo genérico para evitar los errores al trabajar con un tipo de dato o los inconvenientes de trabajar con dynamic.
--Es recomendable que cuando una clase implementa un tipo de dato genérico , la clase que dueña de esa propiedad debe implementar también el tipo de dato genérico.
--También podemos en un Navigator.pop, podemos darle el tipo de dato genérico.
-Hemos convertido a minúscula tanto el element.label, como el query, para así no tener problemas con las búsquedas ni en mayúsculas ni en minúsculas.
final List<SearchPickerItem<T>> filteredList = widget.items
.where((element) =>
element.label.toLowerCase().contains(_query.toLowerCase()))
.toList();
-Todo Widget en Flutter tiene un parámetro Key. En caso de nosotros no pasar un Key, Flutter automáticamente va a pasar el key.
-Los key nos permiten conservar el estado de un Widget cuando este cambia su posición en el árbol de Widgets.
-UniqueKey, nos ayuda a diferenciar los Widgets, pero no nos ayuda a conservar su estado.
-Para conservar el estado tenemos el Key ValueKey.
--Cuando definimos Keys en nuestra aplicaciones, estos tienen que ser únicos, no se pueden duplicar el número.
--ValueKey, aunque compartamos el mismo ValueKey en varios Widgets, esto mantiene el estado cuando el widget cambia de posición en una lista, pero si lo usamos en varios widgets distintos, no va a mantener el estado, para esto tenemos que user GlobalKey.
-Cuando usamos un GlobalKey, podemos compartir el estado entre varios Widgets distintos pero del mismo tipo.
-Cuando vamos a compartir el estado de un Widget con otro Widget, estos tienen que ser del mismo tipo.
-Si usamos un ValueKey para conservar el estado dentro de una lista, tenemos que tener el ValueKey al nivel del elemento padre, para que pueda conservar el estado.
-Esto anterior no aplica para los GlobalKeys, para estos, necesitamos que sean Widgets del mismo tipo, pero no es necesario que el Key, está al nivel del Widget padre o el Widget de mayor nivel.
-Para poder compartir entre modo Portrait y en modo Landscape cuando quiero añadir más elementos con diferentes interfaces y para compartir datos entre estas, para esto puedo usar un GlobalKey.
-Aquí vamos a usar diferentes tipos de interfaces para mostrar como trabajan los GlobalKeys. Vamos a usar un OrientationBuilder para poder mostrar diferentes tipos de Widgets en función de la orientación de la pantalla.
-Para mantener el estado entre el modo vertical y el modo landscape. Esto es util para cuando estamos en una lista y rotamos la pantalla, en estos casos es deseable que se mantenga el estado, se mantenga el lugar por el que estaba en la lista.
-Vamos a acceder al estado de un StatefulWidget usando un GlobalKey.
-Para esto el estado del StatefulWidget, tiene que se público, si es privado no podremos hacerlo.
-Vamos a abrir el Drawer usando programación, para esto tenemos que usar el GlobalKey y acceder al estado al que está relacionado. scaffolkey.currentState.openDrawer(). Para que esto funcione, tenemos que dar el tipo de dato al GlobalKey, este es el tipo de dato en caso de ser un Scaffold. Para saber el tipo de dato, tenemos que ir a la declaración de la clase del tipo de dato para tener esta información.
-Usando el GlobalKey, también podemos acceder a todos los métodos y propiedades públicas de esta clase. Usando scaffoldkey.currentState.showSnackbar
-Vamos a usar un GlobalKey para validar formularios.
-Dentro de TextFormField, podemos definir el parámetro validator para validar el input del TextFormField. En el validator tenemos que retornar un String, en caso de retornar null, esto significa que la validación ha sido válida.
-Dentro de validator podemos realizar diferentes comprobaciones, podemos eliminar los espacios en blanco del texto.
-Aquí tengo que asignarle un Key al Form, dentro del cual vamos a tener nuestro TextFormField.
-Vamos a crear una función encargada de submit. Dentro de esta vamos a usar el método formkey.currentState.validate()
-FormField,
-Uno de los fallos más comunes al iniciar con Flutter, cuando quieren comprobar si ya hay una sesión existente en el dispositivo. Muchas veces se cae en el error de usar initState para realizar esta tarea.
-Usar initState para mostrar un diálogo, navegar a otra pantalla, va a provocar un error, ya que todavía no está renderizaddo nuestra pantalla para realizar operaciones sobre esta.
-Podemos usar un Future.delayed(milliseconds : 100), aquí le asignamos este valor y en la mayoría de los casos evitamos este error, PERO ESTA NO ES LA OPCION CORRECTA.
-Usamos WidgetsBinding.instance.addPostFrameCallback((_){ Aquí llamaríamos a el Widget que queremos mostrar });
AddPostFrameCallback, nos garantiza que el Widget va a ser mostrado en el siguiente Frame después del renderizado de la pantalla, lo que nos ayuda a evitar dichos errores.
-Cuando vamos a realizar tareas con nuestro contexto, tenemos que asegurarnos que se ha mostrando completamente para evitar errores, para esto tenemos que usar addPostFrameCallback para asegurarnos de que no tengamos errores en nuestra aplicación.
-Tengo que definir un globalKey en la clase que vamos a crear. Después tenemos que asignar este GlobalKey a el Widget que deseamos en sus propiedad Key.
-Después tenemos que llamar a nuestro GlobalKey.currentContext.findRenderObject() as RenderBox(). --Esto es para que la respuesta sea de tipo RenderBox().
-Después tenemos que crear una variable de tipo Size. Sería final Size size = renderBox.size();. Después de esto podemos hacer un print($size); para mostrar el tamaño del Widget.
-Podemos crear una función de tipo:
Size _getTextSize(){
final RenderBox renderBox = textKey.currentContext.findRenderObject();
final Size size = renderBox.size;
return size;
}
-Haciendo esto podemos reutilizarla de forma sencilla en distintas partes de nuestro código.
-El paquete Material tiene un widget llamado Tooltip. Este nos muestra un texto cuando presionamos sobre un Widget. Este Widget usa el conetxto para conocer la posición del widget al que va a ser usado para mostrar el texto.
-Usando nuestro renderBox.localToGlobal(), podemos acceder a la posición del Widget.
-Aquí vamos a añadir a la función anterior en renderBox.LocalToGlobal para acceder a la posición.
Size _getTextSize(){
final RenderBox renderBox = textKey.currentContext.findRenderObject();
final Size size = renderBox.size;
final Offset pos = renderBox.localToGlobal(OffSet.zero);
return size;
}
-Tenemos que pasar la posición en zero, en caso de pasar otro valor, esto le añadirá a la posición del Widget lo que especifiquemos en nuestros renderBox. PAra eso usamos renderBox.localToGlobal(Offset.zero) o (Offset(0,0));
-Para mostrar un Widget flotante vamos a usar el Widget Overlay. Esto es un tipo de Widget Stack.
-Para poder acceder a un Widget ancestro o antecesor, tenemos que usar, textkey.currentContext.visitAncestorElements();
-Para acceder al Widget ancestro y imprimir el tipo de Widget que es, podemos realizar lo siguiente.
Size _getTextSize(){
textKey.currentContext.visitAncestorElements(
(element){
print("element.widget
--Aquí para acceder a más ancestros, tendríamos que seguir reescribiendo la parte del código:
element.visitAncestorElements((element) => {
print("element.wdiget ${element.widget.runtimeType}");
return false;
--Podemos usar overlay.of(context); --- De esta forma podemos ir directamente al context sin tener que estar realizando el proceso anterior de ir ascendiendo por los diferentes child de cada uno de los Widgets.
--El widget Overlay es de gran importancia para mostrar objetos flotantes.
-Cuando estamos dentro de un StatefulWidget podemos acceder al context desde cualquier lugar de este StatefulWidget.
-Podemos usar una función así para acceder a nuestro contexto, así como al tamaño de este:
---Esta función siguiente puede causar errores si la ejecutamos en iniState, en ese caso crearemos la segunda función.
Size getSize() {
final RenderBox renderBox = context.findRenderObject();
return renderBox.size;
}
--Usando esta función nos aseguramos de no tener fallos si la vamos a llamar desde initState, addPostFrameCallback nos ayuda a evitar dicho error.
void getSize() {
WidgetBinding.instance.addPostFrameCallback((_) {
final RenderBox renderBox = context.findRenderObject();
widget.onSizeChanged(renderBox.size);
})
}
-Vamos a usar el Scaffold con el contexto para mostrar un Snackbar. Esto también puede ser usado para mostrar un Drawer con el Scaffold.
void showSnackBar(){
final SnackBar snackbar = SnackBar(content:Text("Testing"))
Scaffold.of(context).showSnackBar(snackbar);
}
--Este código va a causar un problema en tiempo de ejecución.
-El contexto aquí es usado para acceder/buscar un Widget padre que contenga un Scaffold. Al buscar y ver que el padre no es de tipo Scaffold, esto va a provocar un error en tiempo de ejecución, y no encontrar un Scaffold como padre de dicho Widget.
--Para solucionar esto podemos usar un Widget Builder, que nos va a ayudar a solucionar este problema. Para poder realizar esta operación podemos hacerlor dentro del Scaffold en floatingActionButton : Builder(
builder: (_) => FloatingActionButton(
onPressed: () => this._showSnackBar(_)))
-La función showSnackbar contiene el siguiente código. De la siguiente forma vamos a abrir un SnackBar y también vamos a abrir el Drawer.
void showSnackBar(BuildContext context){
final SnackBar snackbar = SnackBar(context: Text("hi"));
Scaffold.of(context).showSnackBar(snackbar);
Scaffold.of(context).openDrawer()
}
-Cuando usamos un InheritedWidget, todos los elementos hijos de este, incluso los hijos de los hijos, todos pueden acceder a los elementos de este, tanto propiedad como métodos.
-Creamos una clase que va a extends de InheritedWidget. Cuando extendemos de InheritedWidget, tenemos que override updateShouldNotify.
-Creamos dentro de la clase InheritedMovies, el método que después queremos pasar a otros Widgets. Después en otros Widgets, usamos InheritedMovies y podemos usar el método que hemos creado anteriormente.
-Después podemos acceder a los métodos de la clase padre, para ello tenemos que estar dentro de las clases hijas del elemento padre InheritedMovies. Después desde el Widget hijo para acceder tenemos que hacer:
context.dependOnInheritedWidgetOfExactType<InhertiedMovies>();
--Entre <> tenemos que dar el tipo de dato del Widget padre.
--Después tenemos que hacer inheritedMovies.deleteComment(movie.id, index);
--En la mayoría de las ocasiones se suele usar MoviesProvider en lugar de usar InheritedMovies o cualquier otro nombre que extienda de InheritedWidget.
--Para facilitar el uso del InheritedWidget después en los demás Widgets en los que hagamos uso de este. Podemos crear en la clase Padre del InheritedWidget lo siguiente: static MoviesProvider of(BuildContext context){ return context.dependOnInheritedWidgetOfExactType(); }
--Esto va a facilitar después su uso en otros lugares, ya que tan sólo tendremos que usar: MoviesProvider.of(context).nombre del método que queremos ejecutar.
Por ejemplo:
MoviesProvider.of(context).deleteComment(movie.id, index);
--Debemos tener en cuanta que siempre tenemos que envolver el Scaffold o Widget de mayor jerarquía en nuestro Widget en el provider que estemos usando, en nuestro caso es MoviesPRovider, ya que de no hacer esto vamos a tener errores cuando estemos intentando usarlo, ya que nos dará errores al equivocar los diferentes contextos que estamos usando.