A veces, JSON de una API debe convertirse en un objeto y preferiblemente en un valor inmutable. En Dart es posible, pero requiere mucha codificación para cada uno de los objetos. Afortunadamente, existe un paquete que te ayudará a hacer todo esto, y en este artículo te contaré sobre este método.
Nuestro objetivo:
1. Serialización
final user = User.fromJson({"name": "Maks"});
final json = user.toJson();
2. Usar como valores
final user1 = User.fromJson({"name": "Maks"});
final user2 = User((b) => b..name='Maks');
if (user1 == user2) print(' ');
3. Inmutabilidad
user.name = 'Alex'; //
final newUser = user.rebuild((b) => b..name='Alex'); //
Instalar paquetes
Abra el archivo pubspec.yaml en nuestro proyecto Flutter y agregue el paquete built_value a las dependencias :
...
built_value: ^7.1.0
Y también agregue los paquetes built_value_generator y build_runner a dev_dependencies . Estos paquetes le ayudarán a generar los códigos necesarios.
dev_dependencies :
...
build_runner: ^1.10.2
built_value_generator: ^7.1.0
Guarde el archivo pubspec.yaml y ejecute " flutter pub get " para obtener todos los paquetes necesarios.
Crear valor_construido
Creemos una clase simple para ver cómo funciona.
Cree un nuevo archivo user.dart :
import 'package:built_value/built_value.dart';
part 'user.g.dart';
abstract class User implements Built<User, UserBuilder> {
String get name;
User._();
factory User([void Function(UserBuilder) updates]) = _$User;
}
Entonces, creamos una clase de usuario abstracta simple con un campo de nombre , indicando que nuestra clase es parte de user.g.dart y la implementación principal está allí, incluido UserBuilder . Para crear este archivo automáticamente, debe ejecutarlo en la línea de comando:
flutter packages pub run build_runner watch
o
flutter packages pub run build_runner build
Comenzamos con watch para no reiniciar cada vez que algo cambia en la clase.
Después de eso, vemos que ha aparecido un nuevo archivo user.g.dart . Puede ver lo que hay dentro y ver cuánto tiempo ahorraremos automatizando este proceso. Cuando agreguemos más campos y serialización, este archivo se hará aún más grande.
Veamos lo que tenemos:
final user = User((b) => b..name = "Max");
print(user);
print(user == User((b) => b..name = "Max")); // true
print(user == User((b) => b..name = "Alex")); // false
anulable
Agregue un nuevo campo de apellido a la clase de usuario :
abstract class User implements Built<User, UserBuilder> {
String get name;
String get surname;
...
}
Si lo intentas así:
final user = User((b) => b..name = 'Max');
Entonces obtenemos un error:
Tried to construct class "User" with null field "surname".
Para que el apellido sea opcional, utiliceanulable:
@nullable
String get surname;
o necesita dar apellido cada vez :
final user = User((b) => b
..name = 'Max'
..surname = 'Madov');
print(user);
Colección construida
Usemos matrices. BuiltList nos ayudará en esto :
import 'package:built_collection/built_collection.dart';
...
abstract class User implements Built<User, UserBuilder> {
...
@nullable
BuiltList<String> get rights;
...
final user = User((b) => b
..name = 'Max'
..rights.addAll(['read', 'write']));
print(user);
Enum
Es necesario restringir los derechos para que no tome ningún otro valor que ' leer ', ' escribir ' y ' eliminar '. Para hacer esto, cree un nuevo archivo llamado right.dart cree una nueva EnumClass :
import 'package:built_collection/built_collection.dart';
import 'package:built_value/built_value.dart';
part 'right.g.dart';
class Right extends EnumClass {
static const Right read = _$read;
static const Right write = _$write;
static const Right delete = _$delete;
const Right._(String name) : super(name);
static BuiltSet<Right> get values => _$rightValues;
static Right valueOf(String name) => _$rightValueOf(name);
}
Usuario:
@nullable
BuiltList<Right> get rights;
Ahora los derechos solo aceptan el tipo correcto :
final user = User((b) => b
..name = 'Max'
..rights.addAll([Right.read, Right.write]));
print(user);
Publicación por entregas
Para que estos objetos se puedan convertir fácilmente a JSON y viceversa, necesitamos agregar un par de métodos más a nuestras clases:
...
import 'package:built_value/serializer.dart';
import 'serializers.dart';
...
abstract class User implements Built<User, UserBuilder> {
...
Map<String, dynamic> toJson() => serializers.serializeWith(User.serializer, this);
static User fromJson(Map<String, dynamic> json) =>
serializers.deserializeWith(User.serializer, json);
static Serializer<User> get serializer => _$userSerializer;
}
En principio, esto es suficiente para la serialización:
static Serializer<User> get serializer => _$userSerializer;
Pero por conveniencia, agreguemos los métodos toJson y fromJson .
También agregamos una línea a la clase Derecha:
import 'package:built_value/serializer.dart';
,,,
class Right extends EnumClass {
...
static Serializer<Right> get serializer => _$rightSerializer;
}
Y necesita crear otro archivo llamado serializers.dart :
import 'package:built_collection/built_collection.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'package:built_value_demo/right.dart';
import 'package:built_value_demo/user.dart';
part 'serializers.g.dart';
@SerializersFor([Right, User])
final Serializers serializers =
(_$serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build();
Cada nueva clase construida debe agregarse a @SerializersFor ([ ... ]) para que la serialización funcione como se esperaba .
Ahora podemos comprobar lo que obtuvimos:
final user = User.fromJson({
"name": "Max",
"rights": ["read", "write"]
});
print(user);
print(user.toJson());
final user2 = User((b) => b
..name = 'Max'
..rights.addAll([Right.read, Right.write]));
print(user == user2); // true
Cambiemos los valores:
final user3 = user.rebuild((b) => b
..surname = "Madov"
..rights.replace([Right.read]));
print(user3);
Adicionalmente
Como resultado, habrá quienes dirán que todavía es necesario escribir bastante. Pero si usa Visual Studio Code, le recomiendo instalar un fragmento llamado Built Value Snippets y luego puede generar todo esto automáticamente. Para hacer esto, busque en Marketplace o siga este enlace .
Después de la instalación, escriba " bv " en el archivo Dart y podrá ver qué opciones existen.
Si no desea que Visual Studio Code muestre los archivos " * .g.dart " generados , debe abrir Configuración y buscar Archivos: Excluir , luego hacer clic en Agregar patrón y agregar "** / *. g.dart ”.
¿Que sigue?
A primera vista, puede parecer que tanto esfuerzo no vale la pena, pero si tiene muchas de esas clases, esto facilitará y acelerará enormemente todo el proceso.
PD: Le agradecería y le agradecería mucho que compartiera sus métodos, que le parecen más prácticos y eficaces que el propuesto por mí. Paquetes de
proyectos de GitHub
:
pub.dev/packages/built_value
pub.dev/packages/built_value_generator
pub.dev/packages/build_runner