DEV Community

Toshi Ossada for flutterbrasil

Posted on

3 1 1 1 1

Macros no Flutter/Dart: A Revolução da Metaprogramação que Você Esperava

Macros no Flutter/Dart: A Revolução da Metaprogramação que Você Esperava

Fala Devs,

No ultimo Google I/O tivemos muitas novidades, e uma delas que era uma das mais esperadas foi a confirmação dos Macros para fase experimental (que significa que logo sairá pra stable).

Mas o que são Macros?

Macros são um recurso que visa revolucionar a metaprogramação, ou seja, a habilidade de escrever código que manipula outros códigos.

Mas o que isso significa?

Imagine uma tarefa comum que realizamos com frequência hoje toJson() e fromJson().

Hoje temos duas maneiras de realizar essa tarefa, podemos simplesmente fazer os métodos na mão.

class LogDataOrdinary {
final String? type;
final int? status;
const LogDataOrdinary({required this.type, required this.status});
Map<String, dynamic> toJson() {
return <String, dynamic>{
'type': type,
'status': status,
};
}
factory LogDataOrdinary.fromJson(Map<String, dynamic> map) {
return LogDataOrdinary(
type: map['type'] != null ? map['type'] as String : null,
status: map['status'] != null ? map['status'] as int : null,
);
}
@override
String toString() {
return 'Fazendo no dart Puro: Status: $status, Type: $type';
}
}

O problema desse método é que toda hora que precisamos adicionar um campo novo é no mínimo em 3 lugares que precisamos fazer alteração, um processo que pode ser chato.

Outra forma é utilizarmos uma biblioteca terceira que irá gerar todo esse código com a ajuda do odiado build_runner, como por exemplo o json_serializable

Nesse caso temos alguns fatos que podem frustrar os desenvolvedores, primeiro é que preciso adicionar um “quase” extenso boilerplate

E “outro” problema é que toda alteração precisamos executar novamente o build_runner, as vezes não é um processo tão demorado, entretanto se torna um processo chato de ter que executar o build_runner em toda alteração

Hoje temos que fazer estes processos pois no dart que está embedado no Flutter é nerfado e não temos a possibilidade de utilizar Reflections ou as famigeradas Mirrors alegando que há trade-off que talvez não valeria a pena para habilitar as Mirros no Flutter.

Mas calma, a equipe do Flutter não deu as costas e ignorou este problema. No Flutter Foward que ocorreu ano passado (2023) eles anunciaram que iriam começar a trabalhar em uma nova feature para resolver este problema, eles anunciaram que brevemente iriam lançar as Macros.

Depois de muita espera, chegaram até rolar boatos que eles iriam desistir das macros, neste Google I/O (2024) eles anunciaram que já encontra em fase experimental.

Para começar a testar as macros precisamos ter em mente que ainda está em fase experimental, então não está disponível no canal stable (Espero que você esteja usando esse canal para desenvolver sus apps) então nossa primeira tarefa para habilitar a utilização dos macros é apontar para o canal master

Execute:

flutter channel master

Isso fará com que apontemos para o dart 3.5

E se executarmos o comando flutter channel irá nos apresentar que está apontando para a master com um asterisco (*)

Outra etapa (caso use visual studio code) é apontarmos as extensões do dart e flutter para a pré release, procure as extensões e pressione o botão “switch to Pre-Release Version”

Faça o mesmo procedimento para o Flutter

Precisamos também habilitar no analysis_options.yaml a feature experimental dos macros

include: package:lints/recommended.yaml
analyzer:
enable-experiment:
- macros

Pronto agora já podemos utilizar a macros, algo bom que é que não partiremos de um mundo que teremos que criar todas as macros pois a equipe do Dart em paralelo já está desenvolvendo alguns macros que para utiliza-los basta importar em nosso projeto e para esse processo que citamos acima do toJson() e fromJson() existe o pacote json que está publicado por labs.dart.dev (publicador da equipe do dart para pacotes que estão em experimental)

Para adicionar basta executar um pub add

Para utiliza-lo basta colocarmos a annotation em nossa classe @JsonCodable()

Note que no VS Code ele habilitou um botão “Go to Augmentation” se clicarmos nele podemos ver os macros que foram gerados

E se alterar o código da classe e salvar ele automaticamente vai alterar minhas macros

Impressionante a velocidade

Outro ponto legal é que também podemos fazer nossos próprios macros, vamos então automatizar a geração do toString()

Notamos que iremos necessitar de 2 informações da classe, o nome da classe e os campos

Para criar uma classe iremos criar uma class com o modificador macro e devemos implementar a ClassDeclaration, um ponto importante é que fica obrigatório a criação de um construtor que seja constante.

Feito isso implementamos o método buildDeclarationsForClass que tem dois parâmetros, a clazz (está com Z pois class é uma palavra reservada do dart) que é onde irá conter as informações da classe (como o nome da classe) e também temos o builder que iremos utilizar para gerar o código na macro.

Para recuperar o nome da classe utilizamos o clazz.identifier.name e para recuperar os atributos da classe utilizamos o builder.fieldsOf(clazz) lembrando que ele é uma Feature então precisamos usar o await na execução do método e adicionar o async na função.

Para gerar o código utilizaremos o builder.declareIntype() e geraremos o código em uma String que podemos separar por virgula(,) para que não fique uma linha gigantesca

Ressaltando que a saída do nosso código será “campo: $campo” então é necessário adicionar o & com a tag escape na frente (**).

import 'package:macros/macros.dart';
macro class DataClazz
implements ClassDeclarationsMacro {
const DataClazz();
@override
void buildDeclarationsForClass(
ClassDeclaration clazz, MemberDeclarationBuilder builder) async {
final className = clazz.identifier.name;
final attributes = await builder.fieldsOf(clazz);
builder.declareInType(DeclarationCode.fromParts([
'@override\n'
'String toString() =>',
'\'Classe: $className()',
attributes.map((e) => '${e.identifier.name}: \$${e.identifier.name}').join(', '),
')\';',
]));
}
}
view raw data_clazz.dart hosted with ❤ by GitHub

Para utiliza-lo em nossa classe basta adicionar a annotation que será o mesmo nome do macro criado.

E ele automaticamente irá gerar o código no macro

Agora podemos utiliza-lo em nosso main.dart

void main(List<String> arguments) {
final jsonLog = {"type": "blablabla", "status": 1};
final logMacros = LogDataMacos.fromJson(jsonLog);
print(logMacros.toString());
}
view raw main.dart hosted with ❤ by GitHub

Para executar precisamos adicionar a tag — enable-experiment=macros

Ou podemos também deixar configurado em nosso launch.json

Ressaltando novamente que os macros está em fase experimental e podemos ter alguns bugs que estão sendo corrigidos pela equipe do Dart, mas esperamos que logo(talvez ainda esse ano) já sairá uma versão em em stable para nós deliciar com os macros.

Se quiser ler mais a respeito das macros visite https://dart.dev/go/macros

Tembém gravei um video no canal da Flutter Brasil de como podemos utilizar os macros, confira:


Monorepo (Por Que Essa Estratégia Funciona em Grandes Empresas?)

Entre em nosso discord para interagir com a comunidade e ficar por dentro de todas as nossas publicações: https://www.flutterbrasil.com.br

💡 One last tip before you go

Spend less on your side projects

We have created a membership program that helps cap your costs so you can build and experiment for less. And we currently have early-bird pricing which makes it an even better value! 🐥

Check out DEV++

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️