lunes, 16 de septiembre de 2019

Flutter — Atajos del IDE para Desarrollo Rápido

Créditos: Pooja Bhaumik

Si eres nuevo en el desarrollo de Flutter, debes estar familiarizado con las estructuras anidadas, lo difícil que es agregar o eliminar widgets desde la mitad del código o lo difícil que es encontrar dónde termina un widget y comienza otro. Luego, pasa todo el día haciendo coincidir los corchetes de apertura con los de cierre, o al menos tratando de hacerlo. No estás solo, todos hemos estado allí. Nos llevó tiempo descubrir los atajos, pero tal vez no tengas que pasar por eso porque estoy a tu servicio; y he curado todos esos accesos directos que permiten un desarrollo más rápido y suave en Flutter.
PS. Todos estos atajos trabajan para Android Studio e IntelliJ en Windows. Vienes de iOS? Tal vez este artículo te ayude.

Creando un nuevo widget Stateless o Stateful


Adivine qué? No tienes que escribir manualmente tus widgets clases o sobreescribir las funciones build. El IDE puede hacerlo por tí.
Solo escriba stless para crear un Widget Stateless como esto:


 stful para crear un widget Stateful:


Qué tal si ya creaste un Stateless Widget y añadido muchos hijos, pero luego se dió cuenta de que vá a necesitar un State después de todo? Debería hacer un nuevo widget StatefulWidgety luego manualmente transferir todo su código a el? No tienes que hacerlo!
Puedes sólo situar tu cursor en el StatelessWidget, presionar Alt + Entery hacer clic sobre Convert a StatefulWidget . Todo el código será creado por ti, automáticamente.
¡Hurra!

Más cosas mágicas que puedes hacer con Alt+Enter

Alt + Enteres la varita mágica que puedes usar para desarrollo rápido en Flutter. Puedes hacer clic en cualquier widget , presionar Alt + Entery ver qué opciones tienes para ese widget en particular. Por ejemplo:

Añadir un Padding Alrededor de un Widget

Digamos que tienes un widget que no es un Container, por lo que no tiene una propiedad padding. Quieres dar algo de padding pero tienes miedo de estropear tu estructura del widget. Con nuestra varita mágica, puedes añadir su Padding sin estropear cualquier cosa:

Sólo presione Alt + Enter sobre el widget que necesita un padding alrededor de el, y haga clic en Add Padding Y ahora puedes modificar el padding por defecto para que sea lo que quieres.

Centrar un Widget

Esto no es algo muy extraordinario. Sólo centra tu widget en el espacio disponible. Esto no trabaja dentro de un Column o Row.

Envolver con un Container, Column, Row o cualquier otro Widget
Puedes usar el mismo enfoque para envolver tu widget con un Container . Y ahora, el nuevoContainer se convertirá en el padre de tu Widget.

O, incluso puedes envolver múltiples widgets con un Column o Row en sólo un clic!

O envolverlos con cualquier otro widget:

No te gusta un widget? Remuévalo con la varita mágica.
Si, remover un widget es tan fácil como añadir uno nuevo.

Ver el Código Fuente de Tu Widget
Esta es la mejor cosa acerca de un framework open source. Si quieres saber lo que hay detrás de escena de un widget asombroso una clase, entonces sólo colocas tu cursor sobre este y presionas Ctrl + B . Este actuará como un enlace, llevándote directamente al código fuente de tu Widget donde puedes leer todo sobre él. Flutter también usa comentarios para explicar mucho de éste código, haciendo de gran documentación.

Comprueba las propiedades de un Widget sin dejar el archivo o pestaña
Si deseas comprobar qué cosas asombrosas puedes hacer sin dejar tu archivo y profundizar en los documentos, sólo presione Ctrl+Shift+I para obtener una vista rápida del constructor del widget.

Seleccionar rápidamente un Widget Entero
Muchas veces necesitamos extraer/remover un widget entero e intentamos seleccionarlo manualmente:

Si este es realmente un widget grande, entonces averígüe a qué corchete de cierre pertenece para cual puede ser bastante confuso y no queremos arruinar nuestra estructura entera.
En momentos como estos, me gusta usar este atajo super útil.
Sólo haga clic en el widget que quieres extraer y presione Ctrl+W. El Widget entero es seleccionado por ti sin mover tu cursor una pulgada.

Repare la Estructura de Código

Algunas veces tu código será un desastre. Algo así como esto:

Para las personas como yo que tienen un poco de TOC al ver un código que no tiene una sangría adecuada, esto puede ser una pesadilla.
Ahora, la mayoría de los IDEs tienen está habilidad, (aunque puede que no sea la misma combinación de teclas). Simplemente seleccione todo con Ctrl+A y presione Ctrl+Alt+L para reparar su sangría y reformatear el código.

Ver un esquema de su UI

La mayoría de nuestros Widgets no tienen sólo un hijo en su árbol. Ellos tienen arboles de hijos que tienen sus propios hijos y mucho mas. Si tu Widget tiene hijos anidados tan poco como cuatro de profundidad, entonces puede ser bastante difícil de entender la estructura del código con sólo desplazarse por él. Agradecidamente, tenemos Flutter Outline para venir a nuestro rescate!
Puedes encontrar Flutter Outline en el extremo derecho de tu IDE; este es una de las pestañas verticales y está localizado justo sobre el Flutter Inspector. Cuando lo abres, se ve así:

Ahora, puede ver claramente cual Widget está en dónde, cómo están organizados dentro de la interfaz de usuario y qué widgets tienen otros widgets hijos. ¡Pan comido!

Extraer código en un método

Flutter Outline es una herramienta bastante útil. Puedes hacer la mayorías de las cosas que hizo con Alt + Enter, como envolver con un Column y Centrar un Widget, pero hay cosas aún más impresionantes disponibles en la pestaña Flutter Outline ! Uno de ellos es el botón Extract Method.

Si sientes que estás escribiendo un Widget que se está volviendo demasiado largo y probablemente debería ser un Widget personalizado, entonces, en lugar de cambiar manualmente el código a un método, ¡puedes usar esta herramienta para hacer la magia por ti!

Mover un Widget Arriba y Abajo

Otra cosa loca que puedes hacer con Flutter Outline es si tienes múltiples hijos en un widget, puedes fácilmente reacomodar su orden:

Refactorizar Renombrar
Esta es una herramienta bastante básica que la mayoría de los IDE tienen. Este te permite renombrar un método, widget, clase o nombre de archivo y se asegura que las referencias a el también se renombren. Sólo use Shift + F6 y escriba el nuevo nombre:

Remover Imports sin usar
Así que estás trabajando en un proyecto e importas muchos archivos, pero con el tiempo tu código se optimiza más y más. Eventualmente, es posible que ya no necesite muchas de esas importaciones. Ahora está listo para llevar su código a producción, pero necesita limpiarlo y eliminar todas las importaciones no utilizadas. Tal vez normalmente los elimines manualmente, pero como estoy aquí para hacerte la vida más fácil, aquí tienes una combinación de teclado bastante bonita: Ctrl+Alt+O


Esos son todos los atajos que conozco por ahora. ¡Asegúrate de revisar a menudo para obtener más consejos, trucos y otras cosas geniales!
¿Me perdí un atajo fabuloso para salvar vidas? ¡Comenta abajo!
Comunidad Flutter Dart en Español
Grupo Telegram de Flutter Dart: @flutter_dart_spanish
Mi cuenta en twitter: @unAndroidMas

Shared Preferences: ¿Cómo guardar la configuración de la aplicación Flutter y las preferencias del usuario para su posterior reutilización?

El articulo original en inglés es de Didier Boelens y lo encuentras aquí.
Este articulo describe las nociones de Shared Preferences que le permite a las aplicaciones Flutter (Android o iOS) guardar configuraciones, propiedades y datos en la forma de pares clave-valor, el cual serán persistentes incluso cuando el usuario cierra la aplicación.
Los Shared Preferences son almacenados en formato XML.
Shared Preferences es específica de la aplicación, es decir, los datos se pierden al realizar una de las siguientes opciones:
  • Al desinstalar la aplicación.
  • Al limpiar los datos de la aplicación.

Uso Principal

El uso principal de Shared Preferences es guardar las preferencias de los usuarios, configuraciones, tal vez datos (si no es muy grande) para que la próxima vez que la aplicación sea lanzada, estas piezas de información podrían ser recibidas y usadas.

Visión general de la API

La documentación completa puede ser encontrada en el Repositorio de paquetes de Flutter.

1. Getters

Set < String > getKeys() Retorna todas las claves.
dynamic get(String key) Retorna el valor, asociado con la correspondiente clave.
bool getBool(String key) Retorna el valor boolean, asociado con la correspondiente clave. Si el valor no es un boolean, lanza una excepción.
int getInt(String key) Retorna el valor integer, asociado con la correspondiente clave. Si el valor no es un integer, lanza una excepción.
double getDouble(String key) Retorna el valor double, asociado con la correspondiente clave. Si el valor no es un double, lanza una excepción.
String getString(String key) Retorna el valor string, asociado con la correspondiente clave. Si el valor no es un string, lanza una excepción.
List getStringList(String key) Retorna un conjunto de valores string, asociado con la correspondiente clave. Si el valor no es un conjunto de valores string, lanza una excepción.

2. Setters

Future < bool > setBool(String key, bool value) Guarda un valor boolean y asociado con la clave.
Future < bool > setInt(String key, int value) Guarda un valor integer y asociado con la clave.
Future < bool > setDouble(String key, double value) Guarda un valor double y asociado con la clave.
Future < bool > setString(String key, String value) Guarda un valor string y asociado con la clave.
Future < bool > setStringList(String key, List value) Guarda una lista de valores string y asociado con la clave.

3. Removal

Future < bool > remove(String key) Remueve los pares-clave asociados con la clave.
También, en cualquier setter, el hecho de pasar un valor nulo, corresponde a llamar a eliminar (clave).
Future < bool > clear async Remueve todos los pares-clave enlazados al paquete de la aplicación.

Cómo usarlo?

pubspec.yaml

En el archivo pubspec.yaml, añada el shared_preferences a la lista de dependencias, como sigue:


Uso Práctico
El siguiente trozo de código simula una clase que necesita negociar con una preferencia de usuario (por ejemplo, permitir notificaciones, orden de clasificación).

Conclusiones

Como usted puede ver, es sencillo utilizar este paquete.
El código anterior solo pretende mostrar cómo invocar el paquete. En la práctica, es más probable que no tenga un getter/setter por configuración …
Tenga en cuenta que los Shared Preferences no están cifrados, por lo tanto, no se recomienda colocar datos confidenciales, como una contraseña, por ejemplo.
Estén atentos para nuevos artículos y feliz codificación.

Parseando JSON complejo en Flutter

Créditos: Pooja Bhaumik (https://medium.com/@poojabhaumik)


Tengo que admitirlo, Me estaba perdiendo el mundo gson de Android después de trabajar con JSON en Flutter/Dart. Cuando comencé a trabajar con APIs en Flutter, el uso de parsing en JSON me hizo luchar mucho. Y estoy segura, confunde a muchos de ustedes principiantes.
Estaremos usando la librería incorporada dart:convert para este blog. Este es el método de parsing más básico y sólo se recomienda si está comenzando con Flutter o si está construyendo un proyecto pequeño. Sin embargo, conocer los conceptos básicos de parsing de JSON en Flutter es bastante importante. Cuando seas bueno en esto, o si necesita trabajar con un proyecto más grande, considere bibliotecas generadoras de código como json_serializable, etc. Si es posible, las descubriré en los próximos artículos.
Haga un fork de este proyecto de ejemplo. Tiene todo el código para este blog con el que puedes experimentar.

Estructura JSON #1: Map simple

Vamos a empezar con una estructura jSON simple desde students.json
{
"id":"487349",
"name":"Pooja Bhaumik",
"score" : 1000
}
Regla #1 : Identifique la estructura. Las cadenas Json tendrán un Map (pares clave-valor) o una Lista de Maps.
Regla #2 : Comienza con llaves? Es un map. Comienza con corchetes? Es una Lista de maps.
student.json es claramente un map. (Por ejemplo, id es una llave, y 487349 es el valor de id)
Vamos a hacer un archivo PODO (Plain Old Dart Object?) para esta estructura json. Puede encontrar este código en student_model.dart en el proyecto de ejemplo.
class Student{
  String studentId;
  String studentName;
  int studentScores;

  Student({
    this.studentId,
    this.studentName,
    this.studentScores
 });}
Perfecto!.
¿Era que? Porque no hubo asignación entre los maps json y este archivo PODO. Incluso los nombres de las entidades no coinciden.
Lo sé, lo sé. No hemos terminado todavía. Tenemos que hacer el trabajo de asignar estos miembros de clase al objeto json. Para eso, necesitamos crear un método factory. De acuerdo con la documentación de Dart, usamos la palabra clave factory cuando implementamos un constructor que no siempre crea una nueva instancia de su clase y eso es lo que necesitamos ahora.
factory Student.fromJson(Mapdynamic> parsedJson){
    return Student(
      studentId: parsedJson['id'],
      studentName : parsedJson['name'],
      studentScores : parsedJson ['score']
    );
  }
Aquí, estamos creando un método factory llamado Student.fromJson cuyo objetivo es simplemente deserializar su json.
Soy un poco novato, ¿puedes hablarme sobre la deserialización?
Por supuesto. Vamos a hablarte primero sobre la serialización y deserialización. Serialización simplemente significa escribir los datos (que podrían estar en un objeto) como una cadena, y la Deserialización es lo opuesto a eso. Toma los datos en bruto y reconstruye el modelo de objetos. En este artículo, nos ocuparemos principalmente de la parte de deserialización. En esta primera parte, estamos deserializando la cadena json de student.json
Así que nuestro método factory podría ser llamado como nuestro método de conversión.
También debes notar el parámetro en el método fromJson. Es un map Significa que mapea una clave cadena con un valor dinámico. Es exactamente por eso que necesitamos identificar la estructura. Si esta estructura json fuera una lista de mapas, entonces este parámetro habría sido diferente.
Pero porqué dinamico?
Veamos primero otra estructura json para responder tu pregunta.

name is un Mapmajors es un Map de String y List y subjects es un map de String y List
Dado que la clave es siempre una cadena y el valor puede ser de cualquier tipo, lo mantenemos como dinámico para estar en el lado seguro.
Vea el código completo de student_model.dart aquí.

Accediendo al objeto

Escribamos student_services.dart que tendrá el código para llamar a Student.fromJson y recuperar los valores del objeto Student.

Snippet #1 : imports

import 'dart:async' show Future;
import 'package:flutter/services.dart' show rootBundle;
import 'dart:convert';
import 'package:flutter_json/student_model.dart';
El último import será el nombre de su archivo model.

Snippet #2 : cargar el Asset Json (opcional)

Future _loadAStudentAsset() async {
  return await rootBundle.loadString('assets/student.json');
}
En este proyecto en particular, tenemos nuestros archivos json en la carpeta Assets, por lo que tenemos que cargar el json de esta manera. Pero si tiene su archivo json en la nube, puede hacer una llamada de red en su lugar. Las llamadas de red están fuera del alcance de este artículo.

Snippet #3 : cargar el response

Future loadStudent() async {
  String jsonString = await _loadAStudentAsset();
  final jsonResponse = json.decode(jsonString);
  Student student = new Student.fromJson(jsonResponse);
  print(student.studentScores);
}
In this método loadStudent(),
Line 1 : cargando el json string crudo desde los assets.
Line 2 : Decodificando esta cadena json cruda que tenemos.
Line 3 : Y ahora estamos deserializando la respuesta json decodificada llamando al método Student.fromJson para que podamos usar el objetoStudent para acceder a nuestras entidades.
Line 4 : Como hicimos aquí, donde nosotros imprimimos studentScores desde la clase Studentclass.
Revise su consola Flutter para ver todos sus valores de impresión. (En Android Studio, está bajo la pestaña Ejecutar)
¡Y voilá! Acabas de hacer tu primer parsing JSON (o no).
Nota: recuerde los 3 fragmentos de código aquí, lo usaremos para el siguiente conjunto de parsing json (solo cambiando los nombres de archivo y de método), y no repetiré el código nuevamente aquí. Pero puedes encontrar todo en el proyecto de muestra de todos modos.

Estructura JSON #2 : Estructura simple con arrays

Ahora conquistamos una estructura json que es similar a la anterior, pero en lugar de valores simples, también puede tener un array de valores.
{
  "city": "Mumbai",
  "streets": [
    "address1",
    "address2"
  ]
}
Así que en esta address.json, tenemos una entidadcity que tiene un valorString simple, perostreets es un array deString.
Por lo que sé, Dart no tiene un tipo de datos de matriz, pero en cambio tiene un List, por lo que aquístreets será unList.
Ahora tenemos que verificar la Regla # 1 y la Regla # 2. Este es definitivamente un Map ya que comienza con una llave. Sin embargo, streets sigue siendo List, pero eso nos preocupará más adelante.
Así que address_model.dart inicialmente se verá así
class Address {
  final String city;
  final List streets;

  Address({
    this.city,
    this.streets
  });
}
Ahora, debido a que este es un map, nuestro método Address.fromJson todavía tendrá un parámetro Map.
factory Address.fromJson(Mapdynamic> parsedJson) {
  
  return new Address(
      city: parsedJson['city'],
      streets: parsedJson['streets'],
  );
}
Ahora construya el archivo address_services.dart añadiendo los tres trozos de cigo que mencionamos arriba. Debe recordar poner los nombres de archivo y los nombres de método apropiados. El proyecto de ejemplo ya tiene el archivoaddress_services.dart construido para ti.
Ahora, cuando ejecutes esto, obtendrás un pequeño error. : /
type 'List' is not a subtype of type 'List'
Te digo, estos errores se han producido en casi todos los pasos de mi desarrollo con Dart. Y los tendrás también. Así que déjame explicarte lo que esto significa. Estamos requiriendo un List pero estamos obteniendo un List porque nuestra aplicación no puede identificar el tipo todavía.
Así que tenemos que convertir explícitamente esto a un List
var streetsFromJson = parsedJson['streets'];
List streetsList = new List.from(streetsFromJson);
Aquí, primero estamos mapeando nuestra variable streetsFromJson a la entidad streetsstreetsFromJson sigue siendo todavía List. Ahora creamos explícitamente un nuevo List streetsList que contiene todos los elementos de streetsFromJson.
Compruebe el método actualizado aquí. Observe la declaración de devolución ahora. Puede ejecutar esto con address_services.dart y esto funcionará perfectamente.

Estructura Json #3 : Estructuras anidadas simples

Ahora si tenemos una estructura anidada como esta de shape.json
{
  "shape_name":"rectangle",
  "property":{
    "width":5.0,
    "breadth":10.0
  }
}
Aquí, property contiene un objeto en vez de un tipo de dato primitivo básico. Entonces, ¿cómo se verá el PODO?
Está bien, vamos a romper un poco.
En nuestro shape_model.dart, primero hagamos una clase para Property.
class Property{
  double width;
  double breadth;

  Property({
    this.width,
    this.breadth
});
}
Ahora, vamos a construir la clase para Shape. Estoy manteniendo ambas clases en el mismo archivo Dart.
class Shape{
  String shapeName;
  Property property;

  Shape({
    this.shapeName,
    this.property
  });
}
Note cómo el segundo miembro de dato property es básicamente un objeto de nuestra clase Property vista anteriormente.
Regla #3: Para estructuras anidadas, haga las clases y constructores primero, y luego añada los métodos factory desde el nivel inferior.
Por nivel inferior, queremos decir, primero conquistamos la clase Property y luego subimos un nivel por encima de la clase Shape. Esta es solo mi sugerencia, no una regla de Flutter.
factory Property.fromJson(Mapdynamic> json){
  return Property(
    width: json['width'],
    breadth: json['breadth']
  );
}
Esto fue un simple map.
Pero para nuestro método factory en la clase Shape, podemos sólo hacer esto:
factory Shape.fromJson(Mapdynamic> parsedJson){
  return Shape(
    shapeName: parsedJson['shape_name'],
    property : parsedJson['property']
  );
}
property : parsedJson['property'] Primero, esto lanzará el error de tipo mismatch —
type '_InternalLinkedHashMap' is not a subtype of type 'Property'
Y segundo, hey, acabamos de hacer esta pequeña clase para Property, no veo su uso en ninguna parte.
Correcto. Debemos mapear nuestra clase Property aquí.
factory Shape.fromJson(Mapdynamic> parsedJson){
  return Shape(
    shapeName: parsedJson['shape_name'],
    property: Property.fromJson(parsedJson['property'])
  );
}
Básicamente, estamos llamando al método Property.fromJson desde nuestra clase Property y, a pesar de lo que obtengamos a cambio, lo asignamos a la entidad property. ¡Sencillo! Echa un vistazo al código aquí.
Ejecute esto con su shape_services.dart y estará listo.

Estructura JSON #4 : Estructuras anidadas con Listas

Revisemos nuestro product.json
{
  "id":1,
  "name":"ProductName",
  "images":[
    {
      "id":11,
      "imageName":"xCh-rhy"
    },
    {
      "id":31,
      "imageName":"fjs-eun"
    }
  ]
}
Está bien, ahora estamos profundizando. Vemos una lista de objetos en algún lugar dentro.
Si, esta estructura tiene una Lista de objetos, pero en si mismo es todavía un map. (Vea la Regla #1, y Regla #2). Ahora, refiriéndonos a la Regla #3, vamos a construir nuestroproduct_model.dart.
Entonces creamos dos nuevas clases Product yImage.
Nota: Product tendrá un miembro de dato que es una Lista deImage
class Product {
  final int id;
  final String name;
  final List images;

  Product({this.id, this.name, this.images});
}

class Image {
  final int imageId;
  final String imageName;

  Image({this.imageId, this.imageName});
}
El método factory paraImage será un poco simple y básico.
factory Image.fromJson(Mapdynamic> parsedJson){
 return Image(
   imageId:parsedJson['id'],
   imageName:parsedJson['imageName']
 );
}
Ahora para el método factory de Product
factory Product.fromJson(Mapdynamic> parsedJson){

  return Product(
    id: parsedJson['id'],
    name: parsedJson['name'],
    images: parsedJson['images']
  );
}
Esto obviamente lanzará un error de tiempo de ejecución
type 'List' is not a subtype of type 'List'
Y si hacemos esto,
images: Image.fromJson(parsedJson['images'])
Esto también está definitivamente incorrecto, y te lanzará un error inmediatamente porque usted no puede asignar un Objeto Image a una List
Entonces tenemos que crear un List y luego asignarlo a images
var list = parsedJson['images'] as List;
print(list.runtimeType); //returns ListList imagesList = list.map((i) => Image.fromJson(i)).toList();
list aquí es una List. Ahora, iteramos sobre la lista y mapear cada objeto en list aImage llamando Image.fromJson y luego ponemos cada objeto map en una nueva lista con toList() y almacenarlo en List imagesList. Encuentra el código completo aquí.

Estrutura JSON #5 : Lista de maps

Ahora vamos a photo.json
[
  {
    "albumId": 1,
    "id": 1,
    "title": "accusamus beatae ad facilis cum similique qui sunt",
    "url": "http://placehold.it/600/92c952",
    "thumbnailUrl": "http://placehold.it/150/92c952"
  },
  {
    "albumId": 1,
    "id": 2,
    "title": "reprehenderit est deserunt velit ipsam",
    "url": "http://placehold.it/600/771796",
    "thumbnailUrl": "http://placehold.it/150/771796"
  },
  {
    "albumId": 1,
    "id": 3,
    "title": "officia porro iure quia iusto qui ipsa ut modi",
    "url": "http://placehold.it/600/24f355",
    "thumbnailUrl": "http://placehold.it/150/24f355"
  }
]
La regla # 1 y la regla # 2 me dicen que esto no puede ser un map porque la cadena json comienza con un corchete. ¿Así que esta es una lista de objetos? Sí. El objeto que está aquí esPhoto (o como quieras llamarlo).
class Photo{
  final String id;
  final String title;
  final String url;

  Photo({
    this.id,
    this.url,
    this.title
}) ;

  factory Photo.fromJson(Mapdynamic> json){
    return new Photo(
      id: json['id'].toString(),
      title: json['title'],
      url: json['json'],
    );
  }
}
Pero esto es una lista dePhoto , entonces esto significa que tienes que construir una clase que contega una List?
Así es, podría sugerir eso
class PhotosList {
  final List photos;

  PhotosList({
    this.photos,
  });
}
También note que, este string es una Lista de maps. Entonces, en nuestro método factory, no tendremos un parámetro Map, porque esto es una Lista. Y eso es exactamente porqué es importante identificar la estructura primero. Luego, nuestro nuevo parámetro podría ser un List.
factory PhotosList.fromJson(List<dynamic> parsedJson) {

    List photos = new List();

    return new PhotosList(
       photos: photos,
    );
  }
Esto podría lanzar un error
Invalid value: Valid value range is empty: 0
Hey, porque nosotros nunca podríamos usar el método Photo.fromJson.
Porqué, si nosotros añadimos esta línea de código después de nuestra inicialización de la lista?
photos = parsedJson.map((i)=>Photo.fromJson(i)).toList();
El mismo concepto que antes, simplemente no tenemos que asignar esto a ninguna clave de la cadena json, porque es una Lista, no un mapa. Código aquí.

Estructura JSON #6 : Estructuras anidadas complejas

Aquí está Page.json.
Te pediré que resuelvas esto. Ya está incluido en el proyecto de muestra. Solo tienes que construir el modelo y el archivo de servicios para esto. Pero no concluiré antes de darte sugerencias y consejos (si es el caso, necesitas alguno).
La regla # 1 y la regla # 2 como de costumbre se aplican. Identificar primero la estructura. Aquí hay un map. Así que todas las estructuras de json del 1 al 5 ayudarán.
La regla # 3 le pide que primero cree las clases y los constructores, y luego agregue los métodos de fábrica desde el nivel inferior. Sólo otro consejo. También agregue las clases desde el nivel profundo / inferior. Por ejemplo, para esta estructura json, cree la clase para Image primero, luego Data yAuthor y luego la clase principal Page. Y agrega los métodos factory también en la misma secuencia.
Para las clases Image yData refiérase Estructura Json #4.
Para la clase Author refiérase a Estructura Json #3.
Tip para principiantes: Mientras experimenta con cualquier nuevo assets, recuerde declararlo en el archivo pubspec.yaml.
Y eso es todo para este artículo de Flutter. Es posible que este artículo no sea el mejor artículo de análisis JSON que existe (porque todavía estoy aprendiendo mucho), pero espero que haya comenzado.