Imaginando que temos um aplicativo que agora esta crescendo. Novos desafios vão surgindo com o tamanho crescente e provável que algumas informações serão compartilhadas com as várias páginas, tais como usuário, algum código de identificação, temas… não faltarão dados a serem compartilhados de forma global. Binding vai ajudar nisto.

Com Binding você pode indicar todos os “controllers” que serão utilizado ao longo das páginas, alguns instanciados logo na entrada e outros quando o usuário fizer uma solicitação tardia (Lazy Load) do controle.

Para carga tardia de “Controller” Get possui um método Get.lazyPut(….) que põe o controle na pilha global e deixa lá para ser instanciado quando for chamado.

class <strong>Setup</strong> extends Bindings {
  @override
  void dependencies() {
    /// inicialização
    <strong>Get.lazyPut&lt;Controller>(()</strong> => Controller(0));
    Get.lazyPut&lt;ControllerX>(() => ControllerX(0));
  }
}

Usando Get.lazyPut(..) o chamada ao “binding” vai associado as páginas que vão consumir os controles. A chamada a Setup.dependencias é feito ao abrir uma nova página que referencia a ela:

return <strong>GetMaterialApp</strong>(
      title: 'Flutter Demo',
      namedRoutes: {
        '/': GetRoute(
          page: MyHomePage(title: 'Flutter Demo Get'),
          <strong>binding: Setup(),</strong>
        ),
      },
      initialRoute: '/',
    );

Aqui cabe algumas considerações. Usando Get.put( classe() ), a instancia é criado de imediato e não faz checagem se já foi instanciado em operação anterior, portanto seu uso deve levar em consideração se é desejável manter varias instancias do mesmo controle. Com Get.lazyPut(…) este problema é evitado. Além de por na pilho, ele também checa a sua existência fazendo funcione como um singleton reutilizando a mesma instancia – sendo adequado para controle global do dado. Mesmo nas situações de apontar para o memo binding mais de uma vez, a instância será única.

Ainda cabe um consideração sobre o comportamento “errático” do Get.lazyPut(…) que talvez poderia ser melhorado. Se a chamada do Get.lazyPut(…) for chamada fora de uma situação que se deseja uma instância de imediato – não tem este comportamento, chamou – cria a instância, quando o ideal seria instância somente quando executa o Get.find<>() ou quando fosse utilizar de fato.

Este comportamento compromete ter um único binding para várias situações, impondo a criação de “bindings” diferentes para a diversidade de controles desejados. Se uma página não consome um determinado controller irá exigir que tenha “binding” separado do principal, quando o ideal seria mantê-lo somente o seu tipo na pilha e não a sua instância, permitindo declará-lo mesmo quando ainda não se sabe o momento do uso.

Se gostou, deixa seu “like” na página e comentários para nos motivar a continuar escrevendo… Não gostou, não tem problema, gostaria de saber sua opinião mesmo assim.

Git: https://github.com/amarildolacerda/get_samples

No post #2 vimos como implementar reatividade com GetBuilder, mas não acabou.

Na leitura da documentação de hoje (05/6/20) notei que havia algo que não tinha visto em leituras anteriores: GetX;

Na primeira leitura me pareceu muito interessante, mas precisava de exemplos para entender o processo, então vamos lá tentar dismitificar este tal.

Entendendo, é uma implementação reativo utilizando Stream, ou seja, lá no seu interior temos Stream notificando as alteraçoes. Então, neste formado ele é mais próximo de um BLoC.

Consumindo “extension” para adicionar reatividade

Em outro post já escrevi sobre as “extension”, isto mesmo… os observers de GetX utilizam “extension” para incluir reatividade.

Exemplo: var contador = 0.obs;

No exemplo ‘.obs’ é uma propriedade/método do “int” que gera uma Stream para “contador” ou se preferir teremos um “listener” que notifica seus assinantes.

O que fazer com isto? Como Stream é possível fazer do jeito mais difícil (só para brincar como Stream):

/// testando o '.obs como stream"
Widget everBuilder(RxInterface listener, Widget Function(dynamic) callback,
    {bool condition = true}) {
  return StreamBuilder(
      stream: listener.subject.stream,
      builder: (x, y) {
        if (y.hasData) return callback(y.data.$new);
        return Container();
      });
}

com everBuilder (….) é possível consumir o ‘contador’ como um Stream tradicional do Dart, da seguinte forma:

 @override
  Widget build(BuildContext context) { 
      /// usando StreamBuilder
      return    everBuilder(contador, (x) {
            return Text('$x');
          });
.....

mas, se o que precisa é um “listener” tradicional, então pode usar o “ever(…)” como a seguir:

&nbsp;ever(contador,&nbsp;(x)&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;print('ever:&nbsp;${contador.value}&nbsp;-&nbsp;$x'); });

StreamBuilder – pode esquecer, agora “Obx(…)”

Quando escrevi o “everBuilder” ainda não havia me atentado para uma parte bem rápida da documentação que mostra o “Obx(…)”… então esquece, StsreamBuilder ficou ultrapassado. Mantive o “everBuilder” somente para poder ilustrar, mas poderia muito bem ter descartado e ir direto para o Obx..

Diga, lá… então como usar este tal?

Imagina aquele contador de exemplo “acima”, se desejar que la no meio da construção ele faça uma atualização de um Widget, Obx resolve – ele é um builder que lida com tipos de dados ‘.obs’ como no caso do “contador” fazendo assim:

@override
  Widget build(BuildContext context) {
  ....
  return Obx(() => Text('${contador.value}'));
...

Isto mesmo que você esta imaginando, toda vez que algum código mudar o contador (contador.value ++), irá enviar uma mensagem ao “Obx” indicando que o valor mudou, e portanto precisa refazer o “Text(…)”.

Gostou do Obx? então “deixa um gostei” e deixa os seus comentários ou dúvidas. Assim nos motiva a estudar novos recursos.

O conjunto “Controller” e “GetBuilder” permite que a aplicação se torne reativa, ou seja, ele pode atualizar um dado na página do aplicativo.

Em um primeiro momento quando comparado com “ValueNotifier“, bem simplista, o ganho é quase inexistente. Assim como “ValueNotifier” que exige instânciar o objeto e estabelecer o seu gerenciador de estado “ValueListenableBuilder“, em um primeiro olhar (incorreto), o mesmo ocorria com seus equivalentes “Controller” e “GetBuilder”, vejamos o exemplo:

Criando o “Controller”:

class Controller extends GetController {
  int value;
  Controller(this.value) {
    print('Controller $value foi criado');
  }
  inc([int v = 1]) {
    value += v;
    update();
  }

  dec([int v = 1]) {
    inc(-v);
  }
}

Usando o “GetBuilder”:

Widget build(BuildContext context) {
    return ContainerGroup(
      title: 'GetBuilder',
      width: 150,
      child: GetBuilder<Controller>(
          // init: Controller(0),   // transferido para o binding (Setup() na carga no main.dart)
          builder: (a) => Container(
                alignment: Alignment.center,
                width: 140,
                child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: [
                      Button(
                          width: 40,
                          child: Text('+'),
                          onPressed: () => a.inc()),
                      Text('${a.value}'),
                      Button(
                          width: 40,
                          child: Text('-'),
                          onPressed: () => a.dec()),
                    ]),
              )),
    );
  }

onInit / onClose – Controller

O “Controller” ganho dois eventos que ser sobrescrito (@override) permindo uma separação do código um pouco mais agrupado. onInit é chamado ao criar o GetBuilder (que é um StatefulWidget) equando que onClose é chamado ao ser encerrado.

Os eventos estão encapsulados no “initState” e “dispose” de um StatefulWidget/State, ou seja, então aqueles códigos de inicialização e finalização podem ser transferidos para o “Controller”, entregando mais separação de código ao controller melhorando a leitura e manutenção.

GetBuilder turbinado – sem instanciar o controller

É aqui que o GetBuilder ganha força, NÃO requer uma instância do controller (requer somente na primeira chamada).

Neste ponto simplifica o uso de “controllers” que são utilizados em diferentes classes; o parâmetro “global:true” permite utilizar ou compartilhar a mesma instância do objeto, funcionando como um singleton.

O “GetBuilder” reaproveita o “controller” da primeira chamada armazenada em uma pilha “global”, reutilizando-o nas próximas chamadas.

Como agora a instância é global é bem vindo a propriedade “autoRemove” do “GetBuilder” instruindo a destruição da instância criada sem a necessidade de uma chamada direta ao “dispose”.

https://pub.dev/packages/get

A “package” Get para flutter esta brilhando, então não se perca com as críticas e vamos tentar melhorar o que já é bem interessante.

Porque escrever sobre Get? Bem, pergunta que já me fiz – recorrente. Olhando a documentação, minha primeira impressão é muito confusa e não é a altura da importância e facilidade que introduz – então é preciso uma ajuda para simplificar o texto na mesma altura que a simplificação da “package” nos fornece.

Passo 0 – Instalando o Get

Simples, vai la no arquivo “pubspec.yaml” e inclui o link para “get:”

dependencies:
  flutter:
    sdk: flutter

  get:

Passo 0.0 – Inicializando o Get

A inicialização da estrutura do “Get” consiste basicamente em trocar o “MaterialApp” na entrada do App por “GetMaterialApp“, veja:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    /// GetMaterialApp -> inicializa do Get
    return GetMaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

1 – “snackbar” em uma linha

Seria redundanto repetir a todo momento a simplicidade, então vamos a como gerar a mensagem:

 Get.snackbar(
    'titulo', 
    'mensagem do snakbar',
    snackPosition: SnackPosition.BOTTOM);

a “package” é muito extensa, ficamos para o próximo capítulo…