Serialização para JSON e um objeto imutável. Sobre o pacote built_value para Flutter





Às vezes, o JSON de uma API precisa ser convertido em um objeto e, de preferência, em um valor imutável. No Dart é possível, mas requer muita codificação para cada um dos objetos. Felizmente, existe um pacote que o ajudará a fazer tudo isso e, neste artigo, falarei sobre esse método.



Nosso objetivo:



1. Serialização



final user = User.fromJson({"name": "Maks"});
final json = user.toJson();


2. Use como valores



final user1 = User.fromJson({"name": "Maks"});
final user2 = User((b) => b..name='Maks');
if (user1 == user2) print('    ');


3. Imutabilidade



user.name = 'Alex'; // 
final newUser = user.rebuild((b) => b..name='Alex'); // 




Instale pacotes



Abra o arquivo pubspec.yaml em nosso projeto Flutter e adicione o pacote built_value às dependências :



  ...
  built_value: ^7.1.0


E também adicionar os built_value_generator e pacotes build_runner para dev_dependencies . Esses pacotes irão ajudá-lo a gerar os códigos necessários.



dev_dependencies :



 ...
  build_runner: ^1.10.2
  built_value_generator: ^7.1.0


Salve o arquivo pubspec.yaml e execute “ flutter pub get ” para obter todos os pacotes necessários.



Criar valor_construído



Vamos criar uma classe simples para ver como isso funciona.



Crie um novo arquivo 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;
}


Então, criamos uma classe de usuário abstrata simples com um campo de nome , indicando que nossa classe faz parte de user.g.dart e a implementação principal está lá, incluindo UserBuilder . Para criar este arquivo automaticamente, você precisa executá-lo na linha de comando:



flutter packages pub run build_runner watch


ou



flutter packages pub run build_runner build


Começamos com o relógio para não reiniciar toda vez que algo muda na aula.



Depois disso, vemos que um novo arquivo user.g.dart apareceu . Você pode ver o que há dentro e quanto tempo vamos economizar automatizando esse processo. Quando adicionarmos mais campos e serialização, este arquivo ficará ainda maior.



Vamos verificar o que temos:



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


anulável



Adicione um novo campo de sobrenome à classe de usuário :



abstract class User implements Built<User, UserBuilder> {
  String get name;
  String get surname;
...
}


Se você tentar assim:



final user = User((b) => b..name = 'Max');


Então, obtemos um erro:



Tried to construct class "User" with null field "surname".


Para tornar o sobrenome opcional, useanulável:



@nullable
String get surname;


ou você precisa fornecer o sobrenome sempre :



final user = User((b) => b
  ..name = 'Max'
  ..surname = 'Madov');
print(user);


Coleção Construída



Vamos usar matrizes. BuiltList nos ajudará nisso :



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



É necessário restringir os direitos de modo que não assuma nenhum outro valor além de ' ler ', ' escrever ' e ' excluir '. Para fazer isso, crie um novo arquivo chamado right.dart e crie uma nova 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);
}


Do utilizador:



@nullable
BuiltList<Right> get rights;


Agora, os direitos aceitam apenas o tipo certo :



final user = User((b) => b
  ..name = 'Max'
  ..rights.addAll([Right.read, Right.write]));
print(user);


Serialização



Para que esses objetos possam ser facilmente convertidos em JSON e vice-versa, precisamos adicionar mais alguns métodos às nossas classes:



...
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;
}


Em princípio, isso é suficiente para a serialização:



static Serializer<User> get serializer => _$userSerializer;


Mas, por conveniência, vamos adicionar os métodos toJson e fromJson .



Também adicionamos uma linha à classe certa:



import 'package:built_value/serializer.dart';
,,,
class Right extends EnumClass {
...
  static Serializer<Right> get serializer => _$rightSerializer;
}


E você precisa criar outro arquivo chamado 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 nova classe Built precisa ser adicionada a @SerializersFor ([ ... ]) para que a serialização funcione conforme o esperado .



Agora podemos verificar o que temos:



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


Vamos mudar os valores:



final user3 = user.rebuild((b) => b
  ..surname = "Madov"
  ..rights.replace([Right.read]));
print(user3);


Além disso



Como resultado, haverá quem diga que ainda é preciso escrever muito. Mas se você usar o Visual Studio Code, recomendo instalar um snippet chamado Built Value Snippets e então você pode gerar tudo isso automaticamente. Para fazer isso, pesquise no Marketplace ou siga este link .



Após a instalação, escreva “ bvno arquivo Dart e você poderá ver quais opções existem.



Se você não quiser que o Visual Studio Code mostre os arquivos * .g.dartgerados , você precisa abrir Configurações e procurar Arquivos: Excluir , em seguida, clicar em Adicionar Padrão e adicionar “** / *. g.dart ”.



Qual é o próximo?



À primeira vista, pode parecer que tanto esforço não vale a pena, mas se você tiver muitas dessas aulas, isso vai facilitar e agilizar muito todo o processo.



PS Ficaria muito feliz e grato se você compartilhasse seus métodos, que considera mais práticos e eficazes do que o que propus. Pacotes de



projeto GitHub



:

pub.dev/packages/built_value

pub.dev/packages/built_value_generator

pub.dev/packages/build_runner



All Articles