Flutter: Ganchos de widgets

Criado em 12 dez. 2018  ·  100Comentários  ·  Fonte: flutter/flutter

A equipe do React anunciou ganchos recentemente: https://medium.com/@dan_abramov/making -sense-of-react-hooks-fdbde8803889. Devido à semelhança do Flutter com o React, provavelmente seria interessante ver se isso também se encaixa no Flutter.

A definição:

Hooks são muito parecidos com State de StatefulWidget com uma diferença principal: Podemos ter quantos hooks quisermos em um widget.
Hooks têm acesso a todos os ciclos de vida que um State tem (ou uma versão modificada).

Ganchos podem ser usados ​​em qualquer widget. Ao contrário de State que pode ser usado apenas para um tipo específico de widget.

Hooks são diferentes de super mixins porque não podem gerar conflitos. Hooks são _inteiramente_ independentes e não relacionados ao widget.
Isso significa que um Hook pode ser usado para armazenar valores e expô-los publicamente sem medo de conflitos. Isso também significa que podemos reutilizar o mesmo Hook várias vezes, ao contrário dos mixins.

O princípio:

Hooks são basicamente armazenados dentro do Element em um Array. Eles são acessíveis apenas a partir do método build de um widget. E os hooks devem ser acessados ​​incondicionalmente, exemplo:

FAZ:

Widget build(BuildContext context) {
  Hook.use(MyHook());
}

NÃO:

Widget build(BuildContext context) {
  if (condition) {
    Hook.use(MyHook());
  }
}

Essa restrição pode parecer muito limitante, mas é porque os ganchos são armazenados por seu índice. Não seu tipo nem nome.
Isso permite reutilizar o mesmo gancho quantas vezes desejar, sem qualquer colisão.

O caso de uso

A parte mais útil dos Hooks é que eles permitem extrair a lógica do ciclo de vida em um componente reutilizável.

Um problema típico com widgets Flutter são objetos descartáveis, como AnimationController .
Eles geralmente exigem uma substituição initState e dispose . Mas, ao mesmo tempo, não pode ser extraído em um mixin por motivos de manutenção.

Isso leva a um code-snipper comum: stanim

class Example extends StatefulWidget {
  <strong i="7">@override</strong>
  ExampleState createState() => ExampleState();
}

class ExampleState extends State<Example>
    with SingleTickerProviderStateMixin {
  AnimationController _controller;

  <strong i="8">@override</strong>
  void initState() {
    super.initState();
    _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1));
  }

  <strong i="9">@override</strong>
  void dispose() {
    super.dispose();
    _controller.dispose();
  }

  <strong i="10">@override</strong>
  Widget build(BuildContext context) {
    return Container(

    );
  }
}

Hooks resolve esse problema extraindo a lógica do ciclo de vida. Isso leva a um código potencialmente _muito_ menor:

class Example extends StatelessWidget {
  <strong i="15">@override</strong>
  Widget build(BuildContext context) {
    AnimationController controller = useAnimationController(duration: const Duration(seconds: 1));
    return Container(

    );
  }
}

Uma implementação ingênua de tal gancho poderia ser a seguinte função:

AnimationController useAnimationController({Duration duration}) {
  // use another hook to obtain a TickerProvider
  final tickerProvider = useTickerProvider();

  // create an AnimationController once
  final animationController = useMemoized<AnimationController>(
    () => AnimationController(vsync: tickerProvider, duration: duration)
  );
  // register `dispose` method to be closed on widget removal
  useEffect(() => animationController.dispose, [animationController]), 

   // synchronize the arguments
  useValueChanged(duration, (_, __) {
    animationController.duration = duration;
  });

  return animationController;
}

useAnimationController é um daqueles "ganchos".

Onde uma implementação ingênua de tal gancho seria o seguinte:

Esse código deve ser semelhante a alguém usado para a classe State . Mas isso tem alguns pontos interessantes:

  • O gancho cuida inteiramente do AnimationController , desde a criação até o descarte. Mas também atualizações.
  • Ele pode ser facilmente extraído em um pacote reutilizável
  • Ele também cuida da atualização de duration no hot-reload em vez de criar um AnimationController no initState .
  • Ganchos podem usar outros ganchos para construir uma lógica mais complexa

Desvantagens

Ganchos vem com um custo. O acesso a um valor tem uma sobrecarga semelhante a um InheritedElement . E eles também exigem a criação de um novo conjunto de objetos de curta duração, como encerramentos ou "Widget" como.

Eu ainda tenho que executar um benchmark para ver a diferença real embora


Outra questão é sobre o hot-reload.

Adicionando ganchos no final da lista, se tudo bem. Mas como os ganchos funcionam com base em sua ordem, adicionar ganchos no meio dos ganchos existentes causará uma redefinição parcial do estado. Exemplo:

Passar de A, B para A, C, B redefinirá o estado de B (chamando dispose e initHook novamente).

Conclusão

Hooks simplifica _muito_ o mundo dos widgets permitindo uma maior reutilização de código. Principalmente nos cenários muito comuns como descartar, memorizar e observar um valor.

Eles podem ser usados ​​para substituir inteiramente um StatefulWidget.

Mas eles exigem uma mudança de mente, e a redefinição parcial do estado na refatoração pode ser incômoda.

É possível extrair ganchos fora do Flutter criando elementos personalizados. Não há necessidade de modificar as fontes a partir de agora.

Mas devido à especificidade dos ganchos, se beneficiaria _muito_ de um linter personalizado. Quais pacotes externos não podem fornecer no momento.

Bônus

~Uma implementação de trabalho em andamento está disponível aqui: https://github.com/rrousselGit/flutter_hooks (os recursos mais recentes estão no branch prod-ready ).~

~Um lançamento está planejado em breve como alfa. Mas a implementação atual funciona até certo ponto.~

Disponível aqui https://pub.dartlang.org/packages/flutter_hooks# -readme-tab-

framework new feature

Comentários muito úteis

Só queria dizer - se alguém da equipe Flutter quiser conversar 1:1 sobre Hooks, ficarei feliz em explicar o contexto histórico e técnico por trás do motivo pelo qual estamos adotando-os no React. Este comentário também pode ser interessante: https://github.com/reactjs/rfcs/pull/68#issuecomment -439314884.

Todos 100 comentários

@Hixie @eseidelGoogle

Aqui está um acompanhamento para https://twitter.com/ericmander/status/1070024779015479302 com alguns detalhes do que eu tinha em mente. Caso a equipe do Flutter queira investir mais no assunto

Oi @rrousselGit Eu estava olhando seu pacote flutter_hooks outro dia. Não tenho certeza de quais opções temos para implementá-los no Flutter, mas notei que você está passando métodos de gancho pelo HookContext, enquanto no React, os ganchos podem ser criados separadamente e usados ​​como uma espécie de "mixin de estado". A principal diferença é que flutter_hooks parece exigir que todos os métodos de gancho sejam embutidos no pacote, enquanto o React permite que métodos de gancho sejam totalmente separados.

Parece que isso seria crítico para corresponder às especificações do React. Você tem alguma ideia de como isso pode ser alcançado?

EDITAR :

HookContext foi removido e todos os ganchos agora são métodos estáticos. Portanto, o seguinte agora está desatualizado


Meu pacote faz isso para fins de preenchimento automático, mas não é uma necessidade.

Em vez de

context.useSomeHook()

Nós podemos ter

useSomeHook(context)

Isso não altera estritamente nada além da capacidade de descoberta.

A característica chave é

T HookContext.use<T>(Hook);

O que permite que você construa seus próprios ganchos a partir daí. Contanto que tenhamos esse método, podemos fazer tudo o que o React faz.

Mas devido à especificidade dos ganchos, se beneficiaria muito com um linter personalizado. Quais pacotes externos não podem fornecer no momento.

Eu recomendo que você vote (e adicione seu caso de uso a)
https://github.com/dart-lang/linter/issues/697. Seria ótimo se os pacotes pudessem fornecer seus próprios fiapos personalizados.

Outra questão é sobre o hot-reload.

Este me preocupa. Seria triste se degradássemos a experiência de recarga a quente.

Disponível aqui https://pub.dartlang.org/packages/flutter_hooks

Você pode elaborar quais são as vantagens de assar isso no framework Flutter sobre apenas ter um pacote externo?

Sobre o hot reload, eu o integrei e é fácil de usar.

O único problema real é o #26503, que parece exigir mudanças nas ferramentas para corrigir. E eu não tenho a largura de banda para fazê-lo.


Existem várias pequenas melhorias que podemos trazer para os ganchos integrando-os ao Flutter:

  • Algum aumento de desempenho, por não depender de StatefulElement
  • Refatoração/análise, que atualmente não é possível ou muito limitada usando analyzer_plugin

Mas acho que o maior motivo é: Hooks fazem parte de um trabalho de maior escala no React, que é renderização assíncrona.

Se o Flutter quiser seguir a rota de renderização assíncrona (e deveria), isso exigirá duas coisas:

  • Várias alterações de estrutura
  • Algo semelhante a ganchos

Há também o aspecto óbvio da comunidade. Com 270 estrelas em um mês e um lugar no https://github.com/trending/dart?since=monthly sem que eu realmente os tenha anunciado - os ganchos são definitivamente algo que aumenta o interesse da comunidade.

O objetivo dos ganchos é alterar a sintaxe dos widgets para que as pessoas possam compartilhar a lógica específica do ciclo de vida para publicar.

Com os ganchos sendo oficialmente suportados, isso aumentaria exponencialmente o esforço da comunidade.

Curioso: Você poderia explicar como você fez isso funcionar para recarga a quente?

Eu estendi StatefulElement em vez de ComponentElement, para que eu pudesse ter acesso a reassemble .
Isso permite que a próxima chamada build altere a lista de ganchos, evitando que a reconstrução normal o faça.

Basicamente, se previousHook.runtimeType != newHook.runtimeType em um determinado índice, este gancho e todos os seguintes serão descartados.

A parte "todos os seguintes" é porque um gancho pode usar outros ganchos; então apenas remover um não é suficiente.
De um mês de uso constante, raramente é um problema.

Na verdade, é o oposto, a experiência de recarga a quente parece aprimorada. Como tudo está dentro do método build , um hot-reload sempre aplica as alterações (em oposição a um initState ). Um exemplo típico é AnimationController :

final animationController = useAnimationController(duration: const Duration(second: 1));

Este gancho permite uma recarga a quente suave da duração; que é algo que geralmente requer uma reinicialização a quente.

Comecei a traduzir parte do meu aplicativo principal para remover alguns clichês. Não vejo Hooks como um recurso, mas um padrão completo. São muitos os assuntos que estão apenas esperando para serem implementados e beneficiariam muito a produtividade, mas também a manutenção. Comecei com animação, removendo controladores e adoraria ver Hooks for Firebase ou outros casos comuns.

Na verdade, é o oposto, a experiência de recarga a quente parece aprimorada. Como tudo está dentro do método de compilação, um hot-reload sempre aplica as alterações (em oposição a um initState).

👍 Também tive essa percepção algumas semanas depois de ver a proposta de Sebastian pela primeira vez.

A parte "todos os seguintes" é porque um gancho pode usar outros ganchos; então apenas remover um não é suficiente.

Eu também pensei que em JS talvez uma heurística pudesse ser útil: se o recarregamento a quente fizer com que render ( build do Flutter) seja lançado (por exemplo, devido a tipos incompatíveis), redefinimos todos os estados de Hooks e tentamos novamente. Se falhar novamente, falhamos.

Eu acho que você não tem esse problema devido à digitação forte? Não tenho certeza. O que acontece se você começar a editar com um Hook de estado de string e, em seguida, alterar o código para assumir que é um número?

O que acontece se você começar a editar com um Hook de estado de string e, em seguida, alterar o código para assumir que é um número?

Você quer dizer indo de:

final counter = useState(0)

para:

final name = useState('foo');

?

Em caso afirmativo, o sistema de digitação detecta corretamente que o tipo foi alterado. Isso ocorre porque o tipo useState é na verdade useState<int> vs useState<String> .

JS provavelmente terá mais dificuldade aqui.

Eu realmente não entendo o que isso te salva. Uma declaração fácil de entender, uma linha fácil de entender no initState e uma linha fácil de entender no descarte são substituídas por uma linha menos eficiente e completamente opaca no método de construção (onde o desempenho é crítico). Isso não parece um bom negócio. Você poderia detalhar os benefícios aqui?

(Dito isso, https://pub.dartlang.org/packages/flutter_hooks parece que já implementa tudo aqui, então acho que não precisamos adicionar nada à plataforma?)

Hooks são uma solução para um problema que o React enfrentou por anos. E como o Flutter se inspira muito no React, ele também importou esses problemas.

Eu sou muito ruim em marketing, então eu sugiro fortemente assistir a palestra do React Team sobre isso. A introdução deles começa aqui https://youtu.be/dpw9EHDh2bM.

Seguido de uma comparação lado a lado do antes/depois do Dan Abramov (que respondeu aqui alguns comentários acima).

O ponto é que tudo o que eles ficam nessa conversa também se aplica ao Flutter (incluindo o blog deles sobre mixins serem prejudiciais https://reactjs.org/blog/2016/07/13/mixins-considered-harmful.html)


Dito isto, os ganchos podem ser resumidos na seguinte frase cativante:

Com hooks, você pode 'pub get' uma porcentagem ainda maior de seu aplicativo

Isso ocorre porque os ganchos podem ser inteiramente extraídos do widget e publicados no pub para que outros usem. Isso é algo atualmente impossível com StatefulWidget porque nossa lógica está ligada aos ciclos de vida State .

Outro efeito dos ganchos é que ele altera a forma como o código é agrupado. Atualmente temos o seguinte:

initState() {
  a = A(widget.foo);
  b = B(widget.bar);
}

didUpdateWidget(SomeWidget old) {
  if (old.foo != widget.foo) {
    a.foo = widget.foo;
  }
  if (old.bar != widget.bar) {
    b.bar = widget.bar;
  }
}

dispose() {
  a.dispose();
  b.dispose();
}

Tudo é misturado e dividido em vários ciclos de vida. Fica muito difícil ver se esquecemos de descartar/atualizar uma variável.

Com ganchos, torna-se o seguinte:

final a = useMemoized(() => A(foo));
useValueChanged(foo, () => a.foo = foo});
useEffect(() => a.dispose, [a]);

final b = useMemoized(() => B(bar));
useValueChanged(bar, () => b.bar = bar});
useEffect(() => b.dispose, [b]);

Agora tudo está reagrupado. Fica muito mais aparente se esquecemos de descartar/atualizar uma variável ou não, e podemos facilmente extrair essa lógica em uma função estática que faz tudo internamente.

Os ganchos estão de acordo com DRY. Escrevemos uma vez a lógica para criar/atualizar/descartar uma variável. E nós o reutilizamos em todos os lugares em vez de copiar e colar.

Um exemplo visual (em React):


No geral, embora seja incrível que seja possível implementar ganchos fora do repositório Flutter; Acho que não deveria ficar como está.

É como se o Flutter fornecesse apenas a classe base Widget e fizesse StatefulWidget como uma dependência externa: Não faz sentido.

Flutter é um framework, não queremos oferecer apenas o básico

O que significa que também queremos:

  • fornecer ganchos para todos os aspectos da estrutura que se beneficiariam deles
  • interagir com o inspetor de widgets (a IU atual não é otimizada para esse caso de uso)
  • refatoração/linter
  • fornecer utilitário de teste

Isso requer uma quantidade enorme de trabalho e pode exigir vários PRs no Flutter.
Está muito longe do escopo de um pequeno pacote mantido por um cara em seu tempo livre.

Hooks são DRY e são mixins de lógica/estado de negócios que promovem a colocação limpa de código. A alternativa é a repetição fragmentada em uma base de código.

Embora eu concorde que no papel é mais opaco, o padrão é repetível e compreensível, então depois de um dia de uso fica transparente. É como qualquer outro padrão de programação: opaco quando você inicia, transparente quando você o usa, mas com Hooks a transição é quase instantânea.

Em reação, muitos desenvolvedores pensam que os ganchos fazem com que muitos casos que originalmente dependam de componentes de classe não sejam necessários. Portanto, o componente funcional chegaria ao centro do palco em um futuro próximo.

Minha pergunta ingênua é se o suporte ao widget funcional no flutter também faz sentido. Eu entendo que o widget funcional atualmente não é recomendado devido a não vincular ao seu próprio Element e à falta de BuildContext próprio. Seria benéfico introduzir um novo mecanismo funcional que pode criar os mesmos objetos subjacentes que o widget de classe faz?

@ivenxu Isso é algo que criei também, em outro pacote: https://github.com/rrousselGit/functional_widget

É um gerador de código que gera uma classe a partir de uma função, (com chaves e tudo) e é compatível com ganchos para gerar um HookWidget

A soma de ambos é:

<strong i="11">@hwidget</strong>
Widget foo(BuildContext context, {Duration duration}) {
  final controller = useAninationController(duration: duration);
  useEffect(controller.forward, [controller]);

  final value = useAnination(controller);
  return Text(value.toString());
}

Faz um belo texto que vai de 0 a 1 progressivamente.
Em 6 linhas de código...
O que exigiria mais de 30 linhas com widgets usuais.

E não estou contando as substituições debugFillProperties e operator== geradas automaticamente.

@rrousselGit Eu estava ciente do seu trabalho de widget funcional, coisas boas e obrigado! No entanto, estou falando do suporte nativo do flutter.

A propósito, como funciona a depuração anexada para a geração de código? É possível definir o ponto de interrupção em foo() e examinar a variável local? Como é a pilha de chamadas?

Às vezes, o paradigma funcional para componente/widget é muito mais conciso do que o equivalente de classe. Então, estou procurando ideias se o widget funcional com suporte nativo faria alguma diferença.

Com ganchos fica o seguinte

Eu realmente acho que a imagem do "antes" aqui é muito mais clara e fácil de entender do que a imagem do "depois". Obviamente, porém, essa é uma opinião subjetiva.

É como se o Flutter fornecesse apenas a classe base Widget e tornasse StatefulWidget uma dependência externa: não faz sentido.

Fazer isso estaria muito alinhado com a filosofia de camadas de Flutter. A principal razão pela qual temos StatefulWidget como uma parte central de primeira classe da camada de widgets no framework é que outros recursos no framework dependem dele (por exemplo GlobalKey ), então não podemos. Mas, tanto quanto possível, gostamos de extrair recursos para que não fiquem no núcleo.

Eu acho que faz muito sentido ter isso como um pacote para aquelas pessoas que querem usá-lo. Se houver algo que possamos fazer na estrutura principal para torná-la melhor, mais eficiente, etc., definitivamente devemos fazê-lo.

Eu acho que este é um grande projeto, e estou acompanhando o pacote com interesse.

Dito isto, não estou totalmente claro sobre por que isso precisaria estar na estrutura. Este parece ser um padrão muito útil que algumas pessoas vão adorar e outras vão preferir não usar.

@rrousselGit Suspeito que você teria muito sucesso continuando a desenvolver isso como um pacote separado e abrindo problemas/PRs para a estrutura principal que ajudaria a torná-la mais bem-sucedida. Se há algo que você precisa expor na estrutura que não está atualmente para fazer isso funcionar, vamos lidar com isso. Se houver um bug no framework impedindo que isso funcione, vamos fazê-lo funcionar!

E FWIW, digo isso por ter tido uma experiência semelhante com flutter_svg - que levou a algumas contribuições para o framework e pôde ser útil para muitas pessoas como um pacote.

Acabei de perceber:
Se os ganchos forem implementados no Flutter, um bom candidato é ComponentElement.

Isso significa que, em vez de ter um novo tipo de Widget, ele tornaria StatelessWidget e StatefulWidget compatíveis com ganchos.

Essa abordagem pode permitir que ambas as sintaxes coexistam. Isso pode ser útil considerando que ambas as sintaxes têm seus prós e contras


Estou bem com os ganchos fora do Flutter por enquanto.

Minha única preocupação é que, por não ser oficial, isso reduzirá o uso geral de ganchos. E, portanto, reduza o número de ganchos disponíveis no pub.

Considerando que a maior vantagem dos ganchos é o impacto na comunidade (ganchos personalizados publicáveis ​​no pub), isso prejudica sua utilidade.

Só queria dizer - se alguém da equipe Flutter quiser conversar 1:1 sobre Hooks, ficarei feliz em explicar o contexto histórico e técnico por trás do motivo pelo qual estamos adotando-os no React. Este comentário também pode ser interessante: https://github.com/reactjs/rfcs/pull/68#issuecomment -439314884.

@Hixie ou @dnfield alguém de vocês já procurou @gaearon?

Como um usuário do Flutter que também usa muito o React, posso dizer que os ganchos foram um verdadeiro divisor de águas para o React. Estou escrevendo todo o meu novo código usando exclusivamente ganchos e estou portando quaisquer classes antigas para ganchos enquanto trabalho neles. Os benefícios de ter todo o código relacionado a uma parte específica do seu componente no mesmo lugar são incríveis!

Acho que seria incrível se houvesse um esforço para avaliar se esse paradigma se encaixaria ou não no Flutter ❤️

Eu tinha perdido esse comentário! Acabei de chegar até eles.

Devo admitir que realmente não entendo como Hooks se aplica ao Flutter. Já existem alguns pacotes Hooks se você quiser experimentá-los (alguns são mencionados neste bug acima). Fundamentalmente, eu realmente não entendo qual problema o conceito resolve.

Para mais detalhes sobre minha perspectiva aqui, veja meus comentários anteriores: https://github.com/flutter/flutter/issues/25280#issuecomment -455846788, https://github.com/flutter/flutter/issues/25280#issuecomment - 455847134, https://github.com/flutter/flutter/issues/25280#issuecomment -456272076.

Fundamentalmente, eu realmente não entendo qual problema o conceito resolve.

Hooks são como mixins de estado, mas sem os problemas inerentes aos mixins: não há conflito de variáveis ​​e podem ser usados ​​mais de uma vez.

Hooks podem resolver um dos maiores problemas que o Flutter tem com widgets com estado no momento: lidar com todos esses controladores.

Cada controlador precisa de um initState, didUpdateWidget e um descarte, que são _sempre_ implementados da mesma maneira.

Não podemos extraí-lo para uma classe base/mixin porque podemos precisar de mais de um.
Então, em vez disso, temos que copiar e colar a lógica em todos os lugares.
Mas isso também não é muito bom. É fácil esquecer um passo, como substituir dispose .


O verdadeiro problema com os ganchos é que eles exigem muito trabalho para serem implementados.

A estrutura principal é simples. Mas devido ao seu comportamento único, eles exigem uma profunda integração com as ferramentas de linter e refatoração.

E para uma implementação ideal, alguns recursos de linguagens como tupple/destructuring podem ser necessários.

Não acho que isso realmente aborde os comentários que fiz acima (https://github.com/flutter/flutter/issues/25280#issuecomment-455846788 e https://github.com/flutter/flutter/issues/25280# emitir comentário-456272076 em particular).

Hum, @gaearon provavelmente explicará melhor do que eu, mas da minha perspectiva:

  • O argumento de legibilidade depende do tamanho do widget. Em widgets maiores, os ganchos tendem a ser realmente _mais_ legíveis porque todos os bits relacionados estão juntos em vez de espalhados pelos ciclos de vida.

  • menos erros. Sua API declarativa torna mais difícil esquecer de atualizar/limpar um estado.

  • sobre o "por que eles deveriam ser centrais?", eu gosto da metáfora usada por Dan Abramov.
    Os ganchos são para os widgets o que os elétrons são para os átomos.
    Não faz sentido excluí-los, quando são primitivos usados ​​para construir coisas maiores.
    Não se trata apenas de implementar ganchos. Todo o quadro pode se beneficiar deles.
    Eles podem ser usados ​​para melhorar coisas como animações, formulários e muito mais.

@Hixie , você passou algum tempo usando Hooks ou está planilhando-os para tomar uma decisão? Não tenho certeza de qual % do tempo da equipe do Flutter é gasto escrevendo coisas de estrutura versus escrevendo aplicativos para clientes. Suspeito que as pessoas que escrevem aplicativos para clientes abordarão essa questão de um ângulo ligeiramente diferente de um desenvolvedor de framework. Ou seja, como isso me torna mais eficaz e agrega mais valor aos meus clientes, não se atende aos mesmos critérios de uma solução existente.

Eu introduzi flutter_hooks recentemente junto comfunctional_widget em um de nossos novos aplicativos quentes.

Ele reduziu enormemente o código clichê e depois de refatorar todos aqueles widgets de estado confusos em pequenas funções com ganchos, a base de código encolheu cerca de 70%.

De uma visão de nível superior, um widget funcional com um conjunto de ganchos é como uma simplificação imperativa de combinar vários observáveis ​​usando combineLatest no RX. Mas em vez de declarar explicitamente todos os observáveis ​​no preâmbulo e ter uma função operando neles, os ganchos são muito mais flexíveis porque você os entrelaça no código onde precisa do valor.

Mas, no final, cada widget é como um palco em um fluxo RX.

Eu certamente não acho que devemos remover flutter_hooks. Eu simplesmente não vejo o que nós (a equipe do Flutter) podemos oferecer de mais valioso.

Os ganchos são para os widgets o que os elétrons são para os átomos.

Acho que se estamos seguindo essa metáfora, você deve ver o Flutter como quarks, ou talvez hádrons. Os elétrons de Hooks estão uma camada acima.

@Hixie , você passou algum tempo usando Hooks ou está planilhando-os para tomar uma decisão? Não tenho certeza de qual % do tempo da equipe do Flutter é gasto escrevendo coisas de estrutura versus escrevendo aplicativos para clientes.

Eu mesmo não usei Hooks. Eu escrevo aplicativos com Flutter. Não encontrei nenhuma necessidade de nada como Hooks ao fazê-lo. Como observado acima, porém, não acho que Hooks corresponda ao meu estilo de desenvolvimento (prefiro ver o clichê). Eu definitivamente não gostaria de remover Hooks de quem quiser usá-lo.

Eu simplesmente não vejo o que nós (a equipe do Flutter) podemos oferecer de mais valioso.

Para tornar os ganchos realmente úteis, eles precisam de um plug-in analisador e opções de refatoração personalizadas.
Somente com eles os ganchos realmente brilham.

Hooks permitem uma análise estática muito forte que é impossível com um StatefulWidget típico.
Isso torna o gerenciamento do estado local de um widget mais seguro/mais escalável. Para mim, a redução padrão é apenas um bônus.

Mas:

  • há _muito trabalho_, em APIs de nível muito baixo.
  • a interface do plugin do analisador não é estável, falta, quase não é utilizada e não é recomendada pela equipe (pois há uma análise realizada para cada plugin)
  • flutter em si não usa a API do plugin e, em vez disso, a implementou diretamente no servidor

Portanto, como um pacote mantido pela comunidade, a probabilidade de ter ferramentas completas para ganchos é muito baixa.

Acho que se estamos seguindo essa metáfora, você deve ver o Flutter como quarks, ou talvez hádrons. Os elétrons de Hooks estão uma camada acima.

Se Flutter é quarks, também é uma galáxia.

Na verdade, temos classes de nível muito baixo como Layer .
Mas também há uma grande quantidade de classes de alto nível como Switch .

Todas as bibliotecas Material e Cupertino podem ser extraídas como pacotes, depois Navigator, e formar objetos relacionados. Mas eles não são.
E Hooks estão algumas camadas abaixo dessas.

Não relacionado, mas acredito fortemente que Material e Cupertino não devem estar na biblioteca principal do Flutter porque promove a falta de boas interfaces posicionadas em algum lugar entre os componentes de nível super baixo e de alto nível do Material. Se você já tentou fazer um aplicativo sem a biblioteca de materiais, é incrivelmente difícil.

Ganchos não são tanto para reduzir o clichê. (Também não nos importamos com o clichê.) O principal valor para mim é ser capaz de encapsular e compor lógica stateful.

const [value, setValue] = useState(0)
const debouncedValue = useDebounced(value, 1000)
const interpolatedValue = useSpring(debouncedValue, {
  friction: 10,
  mass: 20
})

Poder canalizar dados entre eles sem se preocupar se eles contêm estado ou não, extrair essa lógica em Hooks personalizados, ou até mesmo aplicar o mesmo Hook várias vezes, é muito expressivo. E, ao contrário das abordagens do tipo Rx, você pode realmente percorrer todo o código sem pesquisar os combinadores.

Para tornar os ganchos realmente úteis, eles precisam de um plug-in analisador e opções de refatoração personalizadas.
Somente com eles os ganchos realmente brilham.

Concordo que precisamos apoiar isso. Isso deve ser um bug arquivado contra o projeto Dart GitHub, no entanto. Eu gostaria que tivéssemos removido os fiapos específicos do Flutter da base de código do Dart, certamente.

Todas as bibliotecas Material e Cupertino podem ser extraídas como pacotes, depois Navigator, e formar objetos relacionados. Mas eles não são.

Sim, isso é provavelmente verdade também. Há razões práticas pelas quais não fazemos isso (por exemplo, isso complicaria o "Hello World", o que tornaria mais difícil começar a usar o Flutter), mas poderíamos considerá-lo mesmo assim. Novamente, porém, essa é uma questão separada que devemos arquivar separadamente se quisermos considerá-la.

O principal valor para mim é ser capaz de encapsular e compor a lógica com estado.

Isso sim é muito valioso. Não está claro que tem que estar na estrutura principal.

Isso sim é muito valioso. Não está claro que tem que estar na estrutura principal.

Isso permitiria a descontinuação de alguns mixins de estado principais.

Por exemplo AutomaticKeepAliveClientMixin está em uma posição estranha.
Temos que chamar super.build , o que é muito contra-intuitivo. E esse super.build retorna null que não funcionará com tipos não anuláveis.
Mas como um gancho, ambos os problemas são resolvidos.

Da mesma forma, não precisaríamos SingleTickerProviderStateMixin e TickerProviderStateMixin pois os ganchos são reutilizáveis ​​quantas vezes desejar.

Alternativamente, os ganchos têm um aspecto importante da comunidade. Se eles não são essenciais, as pessoas são muito menos propensas a criar e compartilhar ganchos personalizados.

Na verdade, não usaríamos ganchos na estrutura principal, pois IMHO eles tornam as coisas menos legíveis. Portanto, tê-los na estrutura principal não ajudaria a remover outro código.

Alternativamente, os ganchos têm um aspecto importante da comunidade. Se eles não são essenciais, as pessoas são muito menos propensas a criar e compartilhar ganchos personalizados.

Se os hooks forem bons, então as pessoas os usarão independentemente de serem do projeto de código aberto Flutter ou, digamos, do projeto de código aberto Hooks.

Na verdade, não usaríamos ganchos na estrutura principal, pois IMHO eles tornam as coisas menos legíveis. Portanto, tê-los na estrutura principal não ajudaria a remover outro código.

Alternativamente, os ganchos têm um aspecto importante da comunidade. Se eles não são essenciais, as pessoas são muito menos propensas a criar e compartilhar ganchos personalizados.

Se os hooks forem bons, então as pessoas os usarão independentemente de serem do projeto de código aberto Flutter ou, digamos, do projeto de código aberto Hooks.

Isso não parece uma má ideia. Hooks podem ser executados como um projeto de código aberto separado. A equipe principal terá prioridade do requisito de mudança principal do projeto de gancho? IMHO É muito importante para o sucesso do projeto de gancho.

O que é o "requisito básico de mudança"?

O que é o "requisito básico de mudança"?

Devtools.
Principalmente sobre plug-ins de analisador, mas também potencialmente um sistema de plug-in em flutter/devtools.

Há muito trabalho envolvido lá, que não pode ser feito pela comunidade.

Na verdade, não usaríamos ganchos na estrutura principal, pois IMHO eles tornam as coisas menos legíveis. Portanto, tê-los na estrutura principal não ajudaria a remover outro código.

Com todo o respeito, mas isso soa um pouco ignorante. Você deve realmente experimentá-los! Eles são muito mais concisos do que dançar com initState/dispose/setState/build para cada aspecto único do estado.

Flutter tem tudo a ver com composição e o uso de ganchos segue naturalmente o padrão de composição, onde usar mixins, por outro lado, é o oposto de composição.

Imagino que continuaremos a considerar tornar o analisador extensível como uma alta prioridade. Infelizmente, existem muitas outras prioridades mais altas, como melhorar a experiência para tarefas básicas de primeira hora e implementar ferramentas para a alteração não anulável por padrão.

Dito isso, não vejo por que não poderia ser feito por não-Googlers. Flutter e Dart são projetos de código aberto e muitas coisas são implementadas por não Googlers.

Dito isso, não vejo por que não poderia ser feito por não-Googlers. Flutter e Dart são projetos de código aberto e muitas coisas são implementadas por não Googlers.

Estamos falando de meses de trabalho em vários projetos, para coisas que a equipe considera "recursos de baixa prioridade", com muitas mudanças importantes. E os projetos impactados são de nível muito baixo com quase nenhuma documentação pública.

O investimento necessário e os riscos de rejeição / ficar preso no meio são muito altos. Especialmente quando não estamos recebendo nenhum dinheiro com isso.

Então, embora eu entenda seu ponto, é extremamente improvável

Existe algum roteiro sobre isso? Você tem alguma idéia se e quando poderia ser possível para os ganchos pousarem em vibração?

Se o Flutter for bem-sucedido (o que espero que seja) em 1 ano, todos os desenvolvedores do React que tentarem integrar o Flutter gritarão quando descobrirem que não há suporte de primeira classe para ganchos aqui.

A equipe Flutter tomou uma boa decisão copiando o modelo React baseado em classes, mas isso é coisa do passado agora, componentes funcionais e ganchos são o futuro, a equipe Flutter deve acordar para essa nova realidade se quiser se manter no mesmo nível .

Precisamente como cgarciae afirmou, a comunidade React está passando para o gancho e o componente funcional daqui para frente. E isso faz com que o flutter pareça desatualizado, pois o modelo de programação e a produtividade não se comparam a outra solução de desenvolvimento de plataforma cruzada.

Tendo experimentado tanto o react como o flutter recentemente, a falta de gancho no flutter deixa um grande buraco em relação à produtividade, consistência e manutenibilidade do apartamento em comparação. Não ajudaria a organização a adotar o flutter em um novo projeto, ou convencer as pessoas a mudar da solução base React para o flutter valer a pena o esforço.

Eu entendo completamente quando alguém está construindo um conjunto de ferramentas, implementação de estrutura de baixo nível, código clichê pode não parecer um problema. Mas se um desenvolvedor de aplicativo precisa escrever muito clichê "desnecessário" para fazer algo funcionar, o efeito é indesejado.

Dependendo do ângulo que as pessoas olham para um problema específico, a compensação/equilíbrio pode parecer diferente. Não fornecer ferramentas adequadas ou determinados recursos pode tornar a implementação interna do framework mais limpa, mas apenas descarrega a responsabilidade do consumidor do framework, torna a implementação do lado do cliente mais complicada e desajeitada do que deveria ser, o que, por sua vez, não ajuda na adaptação de um dado. estrutura. Para um desenvolvedor de aplicativos, produtividade e consistência são importantes, uma abordagem/padrão unificado é importante para o trabalho em equipe e a comunidade. Uma abordagem holística deve sempre considerar as pessoas que vivem do outro lado da cerca.

Hooks devem ser fáceis de implementar, mas widgets funcionais são mais difíceis. Isso exigiria tipos de união no Dart, pois você precisa expressar algo como:

'Widget | Função Widget(BuildContext)'.

widgets funcionais são mais difíceis. Isso exigiria tipos de união no Dart

Isso está longe de ser suficiente.
Ainda há a questão de:

  • como passar parâmetros para funções? Escovando?
  • uma vez na árvore de widgets, dois widgets funcionais devem ter um runtimeType diferente
  • como conectar widgets com devtools? (o método debugFillProperties de widgets)

Já há algum tempo que penso nisso. Não vejo nenhuma solução fácil para widgets funcionais.
A única coisa em que consigo pensar é uma combinação de plugins de geração de código e analisador (para ter uma definição adequada e outras coisas).

Na verdade, a parte de geração de código nos fez desistir do seu pacotefunctional_widget. Ele apenas cria muitos saltos ao revisar o código. Então, isso não funcionaria para mim...

Ele apenas cria muitos saltos ao revisar o código

Daí porque eu mencionei um plugin do analisador.

Com um plugin de analisador personalizado, devemos ser capazes de:

  • verifique se a função não é usada diretamente e a classe é preferida
  • ter um "ir para definição"/"peek"/... adequado, de modo que clicar na classe redireciona para a função

Isso deve corrigir todos os problemas.
Mas vamos voltar para o "ganchos precisam de um sistema de plug-in analisador adequado".

Talvez a introdução de widgets functional não seja necessária:

Digamos que usar ganchos só é permitido dentro do método StatelessWidget build .

De um ponto de vista técnico - realmente não importa (até onde posso pensar agora) se é classe ou função. A parte importante é que você não misture classes com estado e ganchos. Sob o capô, o renderizador de vibração ainda pode contar ganchos e sua ordem se eles forem chamados dentro do método build e gerenciá-lo adequadamente.

No futuro, se a criação de widgets funcionais fosse possível, isso não seria uma quebra de mudança. Além disso, seria compatível com versões anteriores, pois não está quebrando os aplicativos existentes.

Não vejo nenhum problema óbvio em usar ganchos dentro StatelessWidget que tiraria os benefícios de usar ganchos como composição.

Não tenho certeza, no entanto, como isso se compara a casos como useCallback etc de reagir comparando com a capacidade de usar métodos de instância de qualquer maneira

O argumento "devemos adicionar isso porque os desenvolvedores do React ficarão com raiva" é inválido. (veja mais de 500 comentários sobre a discussão JSX).

Flutter é Flutter, e tem sua própria linguagem e padrões de design.
Acho que não devemos adicionar coisas ao framework apenas para torná-lo mais familiar para os desenvolvedores do React, eles podem usar o pacote se quiserem.

Eu li a discussão e acho que @Hixie como desenvolvedor de framework flutter acabou de ver seu lado do problema (implementação e ... coisas) e tinha menos perspectiva sobre como os desenvolvedores de aplicativos flutter (que são seu público-alvo) pensam e usam isso em seu desenvolvimento.

Eu, como desenvolvedor Android, tive esse problema, tento desenvolver uma ótima estrutura de back-end para meus aplicativos sem ter ideia de como os usuários do meu aplicativo experimentarão quando virem minha interface do usuário feia e difícil de entender o UX.

Como outros disseram antes, o gancho é ótimo. não importa quem o apresente, seja o malvado FB ou a Apple.
apenas funciona. ele separa a lógica de estado e seus ciclos de vida. podemos fazer alguns ganchos úteis em cima de outros, publicar widgets, apis, ferramentas independentes, ... de estados de aplicativos, reutilizar nossos pré-produzidos em outros projetos, ....

E eu acho que, se pré-adicionar uma linha em pubspec.yaml para importar material ou cupertino é difícil criar um aplicativo simples de hello-word!!!, não há o que discutir aqui.
Mas como StatefullWidget é muito importante para o ecossistema de flutter, ganchos e sua relevância têm mais importância no futuro do flutter e seu ecossistema e comunidade de desenvolvedores de flutter.

Imagine como o desenvolvimento de aplicativos flutter será mais simples, à prova de bugs, fácil de entender, expansível, legível, depurável, portátil, ... no meio entre o núcleo e alguns widgets complexos como material, ...) especialmente oficial.

Para entender melhor meu ponto, veja como o desenvolvimento do Android foi horrível com java e bibliotecas de suporte e como é chique e doce com Kotlin.
Também abre portas para outras coisas boas, como Coroutines, Flow, suporte AndroidX + Kotlin, novo sistema de interface do usuário (Compose)
Mesmo isso empurrou a comunidade java para iniciar e implementar futuros kotlin em sua eco.

Então, por favor, solte qualquer buffer nos ganchos, e reaja e apenas faça isso

A falta de suporte oficial a hooks é a única razão pela qual eu não mudei de react native.

Ganchos tornam a vida tão mais fácil que eu não quero escrever do jeito antigo nunca mais

Parte surpreendente dos "ganchos nativos" é que podemos portar muitos StatefulWidget internos ou ... para este formulário simples e compreensível.
Além disso, como a equipe do flutter declara o Flutter e o Dart, fáceis de usar, fáceis de entender e com curva de aprendizado rápida. Tudo isso pode ser verdadeiro e melhor com Native Hooks.
E o caminho (initState/dispose/setState/build) não está neste caminho. (podemos precisar deles no back-end da plataforma, mas não para novos desenvolvedores ou mesmo designers que desejam usar código apenas para descrever suas ideias (não lógica complexa))

Minha leitura neste tópico é que há muitas pessoas empolgadas em usar ganchos no Flutter e algumas perguntas em aberto sobre ganchos:

  • Impacto/medição de desempenho - em particular, eficiência de sempre invocar funções versus ver que o estado está ok para reutilizar sem invocar uma nova função.
  • Capacidade (inabilidade?) de jogar bem com teclas globais e/ou mover-se eficientemente pela árvore.
  • Suporte do analisador/fiapos específicos para ganchos.
  • Capacidade de trabalhar também com hot reload como widgets stateful/stateless simples.
  • Necessidade potencial de API de estrutura adicional.

Tenho certeza de que todas essas questões podem ser abordadas, mas abordá-las não é trivial. O problema não é tanto "os ganchos devem estar na estrutura ou os ganchos devem estar em um pacote separado". Até certo ponto, essa decisão já foi tomada - flutter_hooks está disponível no pub.dev há mais de um ano e parece ser um pacote popular. É que tornar o flutter_hooks uma experiência verdadeiramente refinada exigirá algum trabalho e investimento significativos, além do que já foi feito.

Muito desse trabalho _já_ foi feito para as classes principais do framework, e foram necessárias várias equipes de engenharia, bem como vários colaboradores de código aberto, anos para chegar ao ponto em que está. Às vezes, parece que há uma ilusão de "se apenas mesclarmos para repo X, todas as coisas não resolvidas serão resolvidas!" Mas a maneira como essas coisas acontecem é que as pessoas que estão entusiasmadas com elas fazem o trabalho de implementá-las. @rrousselGit já fez muito trabalho em torno disso, e parece que vários outros contribuidores também no repositório de ganchos.

O que estou tentando dizer em poucas palavras é - os ganchos podem funcionar muito bem sem estar no repositório Flutter, estar no repositório Flutter não acelerará a resolução de seus problemas pendentes e que qualquer pessoa e todos que quiserem ver Hooks funcionarem no Flutter está completamente habilitado para fazer isso acontecer.

Bem, na minha opinião, os ganchos devem ser um recurso de linguagem semelhante aos geradores de sincronização / assíncrona .
Não precisa estar relacionado à vibração.

Os problemas que os ganchos resolvem são:

  • gerenciamento de estado declarativo e reativo.
    State é imperativo
  • composição do estado para corrigir DRY.
    Todos esses XXController precisam ser criados+atualizados+descartados e não há como fatorar essa lógica.
  • legibilidade, pois não envolve aninhamento infinito de funções/widgets para alcançar esse resultado

Mas uma sintaxe alternativa poderia ser:

class MyWidget extends HookWidget {
  const MyWidget({Key key, this.title}): super(key: key);

  final String title;

  Hook<Widget> build() hook* {
    final (flag, setFlag) = yield false;
    final (animationController) = yield* useAnimationController(duration: const Duration(seconds: 2));

    // TODO: do something with animationController
    return CheckBox(
      value: flag,
      onChanged: setFlag,
    );  
  }
}

Hook<AnimationController> useAnimationController({required Duration duration}) hook* {
  final (tickerProvider) = yield* useTickerProvider();
  final (animationController) = yield AnimationController(duration: duration, vsync: tickerProvider);
  animationController.duration = duration;

  return animationController;
}

Isso atinge o mesmo efeito que flutter_hook, mas é independente de flutter e com vários fatores de otimização que flutter_hook não pode fazer.

E isso basicamente permite que _qualquer coisa_ use ganchos, não apenas widgets.

Essa é uma ideia interessante. Parece que existem várias solicitações de recursos de linguagem (algo como defer de Go, algo como um destruidor determinístico de C++, alguma capacidade de compor funções implicitamente em objetos) - mas este não é realmente o lugar certo para rastrear isso.

Acho que seria valioso identificar os pontos de dor que as pessoas estão tendo por não ter algo como ganchos no Flutter e arquivar problemas especificamente sobre essa dor sem escolher uma solução para começar. É inteiramente possível, no entanto, que alguns desses pontos problemáticos possam ser apenas coisas que a estrutura está forçando para atingir metas sobre desempenho, qualidade ou trocas de usabilidade (como em, poderíamos melhorar o ponto problemático X no custo de tornar o ponto de dor Y muito pior).

Do jeito que está agora, vou encerrar esta questão. Existe um bom pacote por aí que está abordando essa solução específica, e a solução em si não é algo que esteja pronto para ser incorporado à estrutura em seu estado atual (por motivos descritos acima), e não está claro se a solução realmente beneficiam de serem fundidos no quadro.

@dnfield Hooks funcionam bem 'como uma família', somente quando você tem todas as principais áreas de requisitos cobertas.

Seria inútil usar ganchos de estado se você batesse na parede quando precisar de algum tipo de efeito após as mudanças de estado.

Qualquer gancho por design requer mudanças significativas dentro do núcleo do renderizador ou a introdução de um novo tipo de componente principal.

Tal esforço não vale a pena para apenas um tipo de gancho (ponto de dor), então eu diria que é impossível resolver esse problema como um conjunto de problemas menores. O único gancho não vale a pena mudar de componentes com estado e com certeza não vale a pena introduzir mudanças significativas no mecanismo de renderização.

Se esse problema significa "quero que o Flutter seja mais parecido com o React, mas também como o Flutter", não tenho certeza de quão acionável é - e um problema do GitHub provavelmente não é o caminho certo para rastrear tal coisa de qualquer maneira, mesmo que concedamos tal coisa faz sentido rastrear.

Dito de outra forma: se este é um pedido de reescrita do núcleo do Flutter, esta não é a maneira correta de abordá-lo. Não tenho certeza de qual seria a maneira correta de abordá-lo, mas provavelmente começaria apenas com o fork do repositório e realizando um esforço maciço.

Tenho uma sensação estranha quando os ganchos são apresentados a pessoas que não os conhecem e mantêm outras tecnologias.

Parece que a resposta geralmente é algo como "se você quer que X seja como React, use react".

Minha opinião é que os ganchos são uma solução genérica para muitos problemas de front-end em que você precisa reutilizar lógica de dados complexa. É mais como uma ideia que poderia ser usada em qualquer tecnologia.

Hooks, no entanto, requerem algum tipo de mudança no pensamento sobre o fluxo de dados antes que seu valor seja totalmente visível. Eu acho que pode ser um grande problema ao "solicitar" ganchos em outros frameworks.

Eu acho que a equipe do React fez um ótimo trabalho tentando explicar os ganchos, mas talvez isso crie alguma resistência porque fez os ganchos parecerem fortemente relacionados ao React.

Eu acho que o React é mencionado nos próximos ganchos com tanta frequência simplesmente porque é onde eles foram inventados. React também é a melhor fonte de informação sobre hooks (até agora).

Em geral - eu descreveria o "ponto problemático" como não poder usar um padrão de composição para lógica de dados diretamente no Flutter. Ganchos são apenas um exemplo de permitir isso.

Também entendo que introduzir ganchos é uma tarefa enorme e devo dizer que não estou disposto a empreender.

Pessoalmente, não entendo o argumento "a comunidade tentou corrigir esse problema, então não precisamos fazer nada".

Uma correção da comunidade não significa que não possa ser melhorada pelo Google.
Tome expressões dentro de coleções como exemplo.
A comunidade poderia "corrigir" bifurcando Row/Column/ListView/... para suportar null .
Mas o Google corrigiu isso modificando o idioma.

Além disso, o próprio Flutter diz: Flutter é inspirado no React.
Isso pode ser visto em como uma grande parte de como um Widget é usado é basicamente como uma classe React é usada.

Embora isso não signifique que o Flutter deva corresponder ao React, eu esperaria que a equipe do Flutter pelo menos acompanhasse de perto as atualizações do React e seu ecossistema.
Isso deve pelo menos fornecer uma resposta melhor para por que "não queremos ganchos na estrutura" além do argumento da comunidade.

@rrousselGit @pie6k

Pessoalmente, acho a API Hooks, do jeito que está no React, meio difícil de entender. Eu prefiro os métodos de ciclo de vida e sua nomenclatura descritiva.

Eu sei que este é um "problema comigo", mas eu apoio o comentário do @dnfield dizendo que talvez devêssemos pegar os problemas e implementar soluções de maneira diferente.

Também raramente cheguei a uma situação em que dois widgets separados precisam compartilhar a mesma lógica (é isso que entendi como principal benefício dos Hooks?)

E sobre o Flutter ser inspirado pelo React, tudo bem, mas isso não significa que ele seguirá todos os padrões e arquitetura do React para sempre.
Um dos principais problemas que tive com o React é que há tantas maneiras diferentes de fazer algo, e as "práticas recomendadas" mudam diariamente. Entendo que é a natureza da engenharia de software, mas ainda acho que se há algo ruim nas formas existentes, elas devem ser tentadas para melhorar... em vez de empilhar maneiras diferentes de alcançar a mesma coisa.

Espero que isso ofereça uma perspectiva um pouco diferente e não ofenda ninguém.

Grande respeito por suas contribuições para a comunidade Flutter.

Triste este assunto foi encerrado. Parece que a equipe do Flutter é ignorante em ganchos ou não se importa com o estado atual dos padrões de design (espero que não seja). Isso não é bom para nenhuma tecnologia, as pessoas que investem centenas de horas querem saber se o que estão usando é de primeira ou podem estar no mesmo nível, e agora o padrão mais produtivo são os ganchos e o Flutter está ficando para trás, mas nem sequer foi considerado ou desconsiderado em favor de uma alternativa convincentemente melhor pelos mantenedores.

@cgarciae ninguém é ignorante, @dnfield disse claramente - em vez de tentar espremer as soluções existentes, abra os problemas para os problemas correspondentes que você tem, a equipe do Flutter os avaliará e possivelmente apresentará uma solução que se adapte ainda melhor ao Flutter.

Esse é um recurso que a comunidade vem pedindo fortemente e vai perguntar. Se o problema não for o lugar certo, vocês têm uma solicitação de recurso para registrar o requisito da comunidade?

@SpajicM Estou totalmente bem com "não" como resposta.
O que eu discordo é a justificativa atual.

Algumas notas:

Estes devem dar algum intensivo para discutir sobre ganchos. Não necessariamente implementando-os, mas pelo menos explorando-os.

No final, tudo se resume a um problema de comunicação.

  • A equipe Flutter entrou em contato com a equipe React conforme oferecido por Dan Abramov?
  • A equipe experimentou o gancho no React?
  • Foi considerada uma alternativa aos problemas que os ganchos resolvem?

Nós, como comunidade, não sabemos nada sobre isso.

Levado um passo adiante, acho irônico que a comunidade tenha sugerido dividir esse problema em "quais são os problemas que os ganchos resolvem", quando a própria equipe do React se ofereceu para explicar à equipe do Flutter quais são esses problemas.

@rrousselGit Acho que a ideia por trás disso é ouvir os problemas do lado específico do Flutter, diretamente dos usuários, e tomar isso como ponto de partida, em vez de usar o raciocínio do React, isso não quer dizer que a entrada deles não seria valiosa.

Minha opinião é que seria bom ter uma maneira de separar claramente entre a lógica e o design do widget - então eu concordo que há um problema a ser resolvido , mas o modo como o React faz isso é um pouco confuso para mim e eu desejo que a solução futura do Flutter tenha um mudança de paradigma/curva de aprendizado menos dramática. Não tenho uma ideia em mente, mas tenho certeza de que a comunidade junto com uma equipe poderia criar algo se estivesse com a mente aberta e não se fixasse na implementação de Hooks existente.

Acho que o próximo passo é abrir um problema chamado:

_"Flutter precisa de uma maneira melhor de isolar a lógica do widget"_

Acho que https://svelte.dev/ tem uma abordagem diferente e melhor para resolver esses problemas. Como primeiro passo, a equipe Flutter deve perceber que há um problema e precisamos de uma solução.

Sou novo no flutter e o que sinto é que a API tem muito clichê. Achei que o Dart tem um genérico, parece que estou programando em Go, onde é mais comum/fácil copiar e colar. Espero que haja uma grande refatoração quando o NNDB chegar, especialmente usando o método de extensão ao máximo. A abstração gradual da API por meio de algo como um método de extensão pode valer a pena explorar.

IMHO, a equipe do Flutter está muito interessada na ideia de "tudo é um Widget". Widgets são ótimos para itens visuais e layouts, mas não para obtenção/processamento de dados. Em vez do incômodo FutureBuilder , StreamBuilder , TweenAnimationBuilder etc. Eu preferiria uma API funcional:

Widget build(BuildContext context) {
    final a = useFuture(someFuture);
    final b = useStream(someStream);
    if (a.value == null || b.value == null) {
        return CircularProgressIndicator();
    }

    final value = a.value + b.value;
    final smoothedValue = animate(value, duration: Duration(milliseconds: 100), curve: Curves.easeInOut);

    return Slider(
        value: smoothedValue
    );
}

Na verdade, o Flutter já usa ganchos em alguns lugares. Então, em vez de mais "Fluttery"

MediaQueryGetter {
    builder: (BuildContext context, MediaQueryData data) {
        ...
    }
}

você pode usar
final data = MediaQuery.of(context);

Infelizmente, esse mecanismo (assinatura durante a obtenção) só funciona em conjunto com InheritedWidget.

Não tenho certeza de quanto progresso podemos fazer nisso, mas gostaria de abordar alguns pontos que estou vendo aqui:

  1. É muito mais difícil avaliar uma _solução_ do que avaliar um _problema_ em um problema do GitHub. Quando você apresenta uma solução, há um nível muito mais alto para alcançar a aceitação. Você tem que mostrar que a solução está bem formada, vale o esforço que ela envolve, e que ela resolve algum conjunto de problemas reais proporcional ao nível de esforço que requer (incluindo não apenas prototipagem e implementação inicial, mas também manutenção e qualidade contínuas) . Em outras palavras, um problema em que discutimos um ponto problemático específico em torno do gerenciamento de dados e do estado do widget provavelmente levará a várias soluções, algumas das quais podem ser adotadas na estrutura, algumas podem se tornar pacotes de terceiros e outras dos quais seria bom, mas custaria muito.

  2. Os engenheiros do Google que trabalham no Flutter e no Dart estão sempre procurando torná-lo melhor e geralmente estão sempre ocupados. O mesmo vale para muitos engenheiros que não são do Google que contribuem para o Flutter. O fechamento desse bug não significa que nenhum engenheiro do Google trabalhará para fazer os ganchos funcionarem melhor com o Flutter. O fato de o pacote hooks ser de propriedade de um membro da comunidade não diminui de alguma forma a qualidade do pacote ou seu valor - nem diminui a capacidade de atender a algumas das deficiências identificadas neste bug, por exemplo, melhor suporte para análise/linting

  3. Existem diferentes razões para aceitar ou rejeitar um recurso. Às vezes é que o recurso é muito legal, mas não combina muito com a arquitetura. Às vezes, o recurso parece bom na superfície, mas tem algumas deficiências importantes que não foram abordadas. Às vezes, o recurso é ótimo, caberia com uma quantidade suficiente de esforço, mas a quantidade de esforço que custa supera o valor que oferece aos usuários ou tiraria o tempo necessário para outros recursos ainda mais valiosos. Para o Flutter em particular, às vezes é só que temos uma arquitetura altamente em camadas que permite a extensão por pacotes de terceiros, e muitas vezes é melhor preservar a disposição e permitir que terceiros façam um ótimo trabalho do que incluir algo novo no framework. Nós mesmos fazemos isso em pacotes como o novo pacote de animações que está em https://github.com/flutter/packages/tree/master/packages/animations.

Finalmente, eu estive do outro lado disso. Eu mesmo sou um mantenedor de pacotes e tive propostas de recursos recusadas que tornariam meu pacote mais fácil de manter ou desenvolver. Meu único conselho é que, se você tem um ótimo pacote de que gosta, continue trabalhando nele. Outras pessoas irão reconhecê-lo e ajudar também, esteja ele incluído neste repositório ou não.

Android novo ComposeUI tem gancho como estados: (preview2)

val state = remember { CardDesignerState() } // react: let state = useState(CardDesignerState())
val thing = stateFor<T?> { null } // react: let thing = useState()

também o novo SwiftUI do iOS tem coisas semelhantes (mas é internamente):

// from https://developer.apple.com/tutorials/swiftui/animating-views-and-transitions
Image(systemName: "chevron.right.circle")
                        .imageScale(.large)
                        .rotationEffect(.degrees(showDetail ? 90 : 0))
                        .scaleEffect(showDetail ? 1.5 : 1)
                        .padding()
                        .animation(.easeInOut)

imagine o uso do swiftUI do iOS onCreate, onInit, onUpdate, onExit, ... 🤢

mas o melhor framework (claro que é o Flutter) ainda resiste muito a ideia de usar ganchos, por quê?
Porque parece React/ReactNative!!! ou algumas citações incríveis da equipe de mantenedores:

Eu prefiro ver o clichê

obviamente contra a ideia de flutter para escrever UI com sintaxe declarativa

Acho que o hooks mixin é uma boa solução.
Tenha ganchos em qualquer lugar que gostemos e não toque em códigos e estruturas de desenvolvedores de estilo antigo.
Mas essa ideia e quaisquer outras ótimas ideias precisam de integração pesada com bibliotecas principais e é bom ver mantenedores principais para oferecer suporte de primeira classe a esses recursos.
Também posso imaginar um futuro após a integração de ganchos, que muitas bibliotecas de estrutura de nível superior (como material) suportam fortemente e até usam nelas.

Flutter poderia ser mais bonito:

// this is just scratch, not a complete and true use-case
build(context) {
    final angle = useAnimation(2*PI, 0, 5 /*seconds*/);
    return Image.from(myAwesomeImage)
        .padding(8)
        .scale(2.5)
        .rotate(angle);
}

@HKhademian concordo totalmente. A falta de suporte de primeira classe para qualquer coisa do tipo gancho (ou em outros termos - qualquer coisa que permita a composição adequada da lógica de negócios reutilizável) é na verdade a única razão pela qual não escolhi o Flutter para meu último projeto.

2. O fato de o pacote hooks ser de propriedade de um membro da comunidade não diminui de alguma forma a qualidade do pacote ou seu valor - nem diminui a capacidade de atender a algumas das deficiências identificadas neste bug, por exemplo, melhor suporte para análise /linting

Os membros da comunidade fantasma pacotes? sim. O Google está indo para o Flutter do núcleo fantasma? Bem, com base em sua posição aqui, parece que teria que ser uma possibilidade. Afirmar que o Flutter central é tão viável para consumo quanto os pacotes da comunidade é... errado na melhor das hipóteses e uma mentira na pior.

Além disso, foi demonstrado repetidamente que, a menos que haja adesão de @Hixie , que demonstrou afinidade em usar preferências pessoais para substituir solicitações da comunidade, não importa o que a comunidade deseja ou precisa. Sem buy-in, isso não acontece. E se não for esse o caso, então o processo de decisão é muito opaco para ver o contrário.

O que me interessa é que as pessoas que decidem se os recursos vivem ou morrem parecem ter muito pouca experiência comercial na criação de aplicativos móveis. Esse ponto de vista sempre fornecerá mais informações sobre qual açúcar misturar no Flutter.

Acho que precisamos de um processo formalizado para decidir quais recursos serão incorporados ao Flutter que coloque mais peso na comunidade para contribuir com o processo de tomada de decisão. Obviamente não 100%, mas agora parece que é cerca de 5%, e uns 5% super esquisitos. @timsneath

É muito mais difícil avaliar uma solução do que avaliar um problema em um problema do GitHub

A equipe do React se ofereceu para explicar seu raciocínio por trás de Hooks no início desta edição.
Isso deve cobrir totalmente a necessidade de explicar por que os ganchos são necessários, já que ninguém está em melhor posição para explicar os ganchos do que a própria equipe do React.

Luke, eu respeito e aprecio sua paixão, mas por favor, pare com os ataques ad hominem. Estamos todos trabalhando para tentar construir a melhor plataforma possível, fazendo todos os tipos de trocas com base na capacidade de recursos, pesquisa de usuários, participação das partes interessadas, necessidades do cliente e opinião da comunidade.

Seja gentil, por favor. https://github.com/flutter/flutter/blob/master/CODE_OF_CONDUCT.md

Nenhum ataque ad hominem pretendido, obrigado pelo feedback. De acordo com o CoC, talvez devêssemos nos reunir para discutir o que estou tentando comunicar.

As pessoas que discordam devem se reunir, tentar entender os pontos de vista uns dos outros e trabalhar para encontrar um design que atenda às preocupações de todos.

Houve alguma atualização sobre isso, ou onde podemos coordenar uma solução? Estou passando por isso com meus projetos Flutter, especialmente com animações e transições. Hooks ajudam a encapsular este estado initState e dispose , entre outros, em uma única função e apenas usar essa função em vez de configurar tudo.

Parece, pelo menos para um observador externo/usuário do Flutter em vez de um mantenedor de plugins, que houve preocupações legítimas que não parecem estar sendo ouvidas, como o outro problema com Flutter Overlays (#50961 ), e eles parecem ser descartados como não sendo problemas reais para os usuários, e não para os mantenedores. Novamente, sem desrespeito aos mantenedores, esta é apenas a minha opinião como alguém lendo casualmente este tópico, não estou muito familiarizado com outros problemas que possam ser diferentes.

Com isso dito, como podemos proceder de forma mais geral ao pensar em soluções para problemas que os usuários parecem ter legitimamente? Eu sei que Rust tem um sistema RFC interessante que parece funcionar bem para projetar novas soluções.

@satvikpendem O processo RFC aqui é registrar um bug descrevendo o problema e, depois que o problema for claramente descrito, discutir possíveis soluções.

Parece que os problemas foram explicados até agora, então as soluções podem ser discutidas agora, passando pelo seu comentário. O que podemos fazer a seguir, existem outras soluções que funcionam melhor do que ganchos ou algo semelhante?

Se você me informar sobre o bug#, posso informar se o problema descreve um problema com detalhes suficientes para que faça sentido começar a discutir possíveis soluções.

@hixie https://github.com/flutter/flutter/issues/51752

Também acho que devemos dar seguimento ao comentário de Dan: https://github.com/flutter/flutter/issues/25280#issuecomment -456404333
Qual foi o resultado dessa discussão?

Oi Remi, muito obrigado pelo bug #51752. Eu sei que você investiu muito tempo neste espaço e contribuiu com pacotes altamente valiosos aqui. Obrigado x1000 por isso!

Para reiterar o comentário de @dnfield , parece que ainda não nos alinhamos em torno do espaço do problema e seu significado. O bug acima é um passo útil para esse objetivo e/ou para discutir possíveis abordagens para o problema. Sua pergunta de acompanhamento aqui assume que uma conversa com a equipe do React é o primeiro passo a partir daqui, mas se ainda não temos alinhamento no espaço do problema, isso parece prematuro.

Talvez possamos mostrar exemplos completos de aplicativos que têm esses problemas que seriam corrigidos com ganchos. Nós não os reescreveríamos necessariamente com ganchos, mas apenas mostraríamos quanta cópia e colagem existe. Isso seria algo passível de discussão, @timsneath? Ou estaria pensando em outra coisa? Estou tentando descobrir como mostrar o espaço do problema da forma mais clara possível.

:wave: @timsneath
Para ser honesto, não entendo muito bem por que discutir com a equipe do React seria prematuro.

Mesmo sem a comunidade solicitando ganchos, uma discussão com a equipe do React ainda seria muito valiosa.
O React compartilha muitas semelhanças com o Flutter, e eles têm alguns anos extras de experiência lidando com objetos semelhantes a Widgets.

Discutir com eles só pode trazer benefícios para ambas as partes.

Como um exemplo:
Depois de criar flutter_hooks, Dan me contatou para discutir como eu lidei com o hot-reload para hooks.
Minha resposta foi "Não havia quase nada a fazer porque o Flutter usa uma linguagem digitada".
Alguns meses depois, eles melhoraram o hot-reload do React gerando algo semelhante aos tipos com o Babel

Tenho certeza de que, se a equipe do Flutter e do React discutisse, muitas dessas interações poderiam acontecer, e ambas as tecnologias progrediriam.

Algumas perguntas abertas que podem ser feitas sem um problema específico:

  • Por que ganchos?
  • Por que modo suspense/concorrente?
  • Por que portais?

@satvikpendem O processo RFC aqui é registrar um bug descrevendo o problema e, depois que o problema for claramente descrito, discutir possíveis soluções.

Isso significa que quaisquer discussões que não sejam um bug são ignoradas? Eu pensei que o propósito do RFC era para coisas que não são bugs, mas geralmente para expandir coisas mais subjetivas, como experiência do desenvolvedor ou semântica de ferramentas.

Se alguém disser: "Acho que devemos ter ganchos porque eles estão se tornando idiomáticos em várias estruturas e as pessoas relatam experiência e produtividade aprimoradas", essa é uma discussão válida, mas não é um bug reproduzível.

Estou absolutamente certo de que ninguém disse que "discussões que não são um bug serão ignoradas". Vamos trabalhar juntos de boa fé, tratando uns aos outros com respeito e cortesia. Estamos todos fazendo o nosso melhor aqui, mas com muitos outros bugs, designs e ideias concorrentes competindo por nossa atenção :)

@lukepighetti , ironicamente, "devemos ter ganchos porque outros frameworks os têm" é exatamente o tipo de conversa que estamos tentando evitar, na verdade - porque leva a um design que é, por definição, otimizado para as necessidades de outros frameworks. É muito útil no processo descrever o problema que estamos tentando resolver no contexto do Flutter, pois isso nos ajuda a concordar se o problema é o mesmo ou não, se a solução deve ser a mesma etc.

@rrousselGit -- claro, existem algumas conversas gerais úteis que poderíamos ter com a equipe do React. Congratulo-me com a oferta, e talvez devêssemos fazer isso em algum momento. Eles têm muita inteligência nessa equipe, com certeza, e sua oferta é muito graciosa e gentil. No momento, estaríamos conversando com eles principalmente porque você nos disse, e não porque temos perguntas específicas que estamos informados o suficiente para discutir :)

E também, apenas um lembrete: "a equipe Flutter" são apenas as pessoas que contribuem para o Flutter. Alguns colaboradores trabalham para o Google, e alguns colaboradores desempenham um papel mais importante no controle da arquitetura do projeto, mas você também está na equipe do Flutter, assim como qualquer outra pessoa que contribui para o projeto :)

Como sempre - obrigado - por sua paciência em discutir isso, por suas contribuições para a comunidade, por continuar a impulsionar o projeto com ideias criativas neste espaço!

Para esclarecer, "bug" como o usamos é um termo geral que se refere a qualquer coisa que possa levar a uma mudança. Pode ser um caso de uso, pode ser uma ideia, pode ser um erro de lógica, pode ser um erro de digitação na documentação, algo confuso no site, o que for.

Talvez possamos mostrar exemplos completos de aplicativos que têm esses problemas que seriam corrigidos com ganchos. Nós não os reescreveríamos necessariamente com ganchos, mas apenas mostraríamos quanta cópia e colagem existe.

@satvikpendem Se o problema for "minha aplicação tem muito clichê", então certamente é um bug que você deve registrar e podemos discutir como melhorar as coisas. Mencione o bug # aqui para que possamos continuar a conversa em seu bug.

Obrigado pelo comentário @Hixie. Meu problema é amplamente coberto pelo mesmo bug que @rrousselGit mencionou (# 51752), então acho que não tenho mais a adicionar com base no que li nesse problema.

@timsneath Não tenho certeza se entendi seu comentário para @lukepighetti , parece que descrevemos o problema no contexto do Flutter algumas vezes diferentes, como este problema, #51752 e outros. O que mais precisaríamos incluir? Como podemos ajudá-lo a estar mais informado sobre esse espaço de problemas, de modo que, se falarmos com a equipe do React ou com outras pessoas, você tenha conhecimento suficiente para fazer perguntas informadas, como você diz?

Concordo que não devemos copiar coisas de outros frameworks só porque o React as tem, por exemplo, então pode ser útil ver outras soluções para esse problema de duplicação de código. O fundador do Vue @yyx990803 postou alguns de seus pensamentos no RFC do Vue (https://www.github.com/vuejs/rfcs/tree/function-apis/active-rfcs%2F0000-function-api.md) o que seria útil para passar. Um olhar particular nas seções sobre quais problemas são resolvidos e por que eles discordam respeitosamente sobre a API baseada em classe é útil para ler.

Obrigado por esclarecer @Hixie , não entendi essa definição mais ampla (possivelmente interna?) de 'bug'.

@timsneath Não tenho certeza se sigo. Outro grupo de pessoas, os desenvolvedores de reação principal, já identificaram e comunicaram um conjunto de problemas, criaram uma solução em uma estrutura arquiteturalmente semelhante e muitas equipes de front-end em várias estruturas estão relatando sucesso. Não vejo indicação de que este seja um problema do GitHub de "solução antes do problema". Parece que @Hixie não concorda que há um problema a ser resolvido, e parece que isso está sendo baseado em escolhas estilísticas ou de manutenção que não refletem os benefícios da experiência do desenvolvedor. Digo isso com o maior respeito ao tentar entender de onde vem a relutância a esta RFC.

Eu normalmente não recomendo recursos de papagaio, mas o RFC para ganchos não é sem técnica prévia com boa justificativa. A técnica anterior está disponível na equipe principal de reação, e qualquer justificativa que encontrarmos será repetir o que eles podem e comunicaram. @rrousselGit parece estar defendendo que vocês marquem uma reunião com eles para discutir esse tópico, pois eles podem fornecer muito mais informações do que podemos em um problema do GitHub.

Como meta-questão, para mim, pessoalmente, seria útil se houvesse um processo concreto para obter RFCs amplas incorporadas ao roteiro flutter/flutter que são apresentados pela comunidade. Nós temos um ditado que os stakeholders externos são os que querem o trabalho difícil e necessário, por isso eles têm que ser incluídos e levados a sério. Externo no contexto de flutter/flutter seria não-equipe, não-google, não-GDE.

Documentei nosso processo RFC em nosso wiki.

Externo no contexto de flutter/flutter seria não-equipe, não-google, não-GDE.

Por definição, se você estiver enviando um RFC, você é um membro da equipe.

Meu problema é amplamente coberto pelo mesmo bug que @rrousselGit mencionou (# 51752)

Nesse caso, recomendo participar dessa discussão; este assunto em particular está encerrado, mas aquele está em aberto. Houve algumas descrições de propostas nessa edição, embora nenhuma pareça fazer um bom trabalho. Ainda não houve uma descrição particularmente clara de "ganchos" lá, só foi mencionado de passagem.

Eu ainda não entendo por que devemos explicar o problema quando ambos os problemas e as soluções propostas são muito claramente documentadas pelo React e Vue.

A RFC de ganchos contém cerca de 1400 comentários, vídeos de introdução, documentações e artigos de muitas pessoas inteligentes.

Podemos discordar sobre a solução para esses problemas. Mas não deveria haver necessidade de explicar os problemas

O problema foi explicado em #51752, não foi? Não é esse o problema?

(Quanto ao motivo: porque apontar uma equipe de desenvolvimento para um RFC de 1400 comentários para um produto diferente não é uma maneira eficaz de se comunicar com essa equipe de desenvolvimento. Desculpe se parece que estou sendo obtuso.)

Desculpe por pingar o megathread. Só queria dizer para quem está acompanhando este que deixei mais alguns pensamentos da perspectiva do React em https://github.com/flutter/flutter/issues/51752#issuecomment -665380355 e ficaria feliz em responder mais perguntas se isso seria útil.

Eu também queria expressar esperança de que isso (e tópicos relacionados) possa permanecer civilizado e não pressionar os mantenedores com argumentos como "funciona para o React". React e Flutter têm diferenças significativas no modelo de programação, e React Hooks especificamente dependem bastante de algumas nuances nossas. Eles também têm algumas peculiaridades como resultado que muitos podem não aceitar.

Eu mudei minha opinião sobre ganchos. Eu acho que eles são um padrão fantástico, mas acabei de terminar um contrato React Native e os ganchos (na minha opinião) fragmentaram o sistema de desenvolvimento horrivelmente por muito pouco ganho. Atualmente, o Flutter está usando um padrão muito semelhante aos componentes da classe React e há uma tonelada de ferramentas construídas em torno dele. Se a estrutura mudar para ganchos como a solução primária de gerenciamento de estado, ela quebraria todo o trabalho existente e os padrões mentais que os desenvolvedores do Flutter usam para obter muito pouco ganho.

Acho que há um argumento de que os ganchos são um padrão superior para o desenvolvimento produtivo, mas acho que há um argumento mais convincente (como o argumento central do dartfmt) de que a consistência ao longo do tempo é melhor do que "melhor".

Também devo observar que, ao lidar com novos desenvolvedores React, muitas vezes nos deparamos com obstáculos com ganchos criando resultados inesperados e temos que ensiná-los a usar componentes de classe primeiro. (Uma boa comparação é o fenômeno de que novos desenvolvedores têm mais facilidade em usar loops for em comparação com métodos de coleta como map/reduce/filter/fold). Ganchos são um padrão avançado e às vezes tomamos isso como garantido. O frustrante aqui é que a comunidade React está rapidamente eliminando a documentação e o suporte para padrões de componentes de classe, tornando mais difícil oferecer essa educação ou opção para novos desenvolvedores.

Eu mencionei na outra edição #51752 que talvez devêssemos trabalhar na criação de uma versão de ganchos mais específica do Flutter, já que os próprios ganchos parecem ter algumas desvantagens, como o padrão useEffect(() => ..., [] ) para renderização única. @Hixie fez uma versão interessante com o padrão Property e PropertyManager que parece fazer algo semelhante aos ganchos, mas pode não ter essas desvantagens. Devemos procurar mais alternativas aos ganchos porque, pelo menos para o Flutter, parece que há algo que funciona melhor do que os ganchos do estilo React, mas ainda resolve os mesmos problemas.

Esta página foi útil?
0 / 5 - 0 avaliações