Gerenciamento de Estado com GetX no Flutter

O get, e não get_it, é um pacote muito completo, contendo os três pilares ( Gerenciamento de Estado, Gerenciamento de Rotas e Gerenciamento de Dependências ).

Ele conta com outras diversas soluções, como um cliente http GetConnect, utilidades GetUtils e etc...

Porém nós iremos falar sobre o GetxController, existem algumas formas de fazer o gerencimento de estado com o get, podemos optar pelo reativo ou simples.

Hoje, iremos abordar o mais simples. Então vamos lá, depois de você ter instalado o get no seu projeto no arquivo pubspec.yaml podemos ir diretamente para o código.

Vamos utilizar o código padrão de boas-vindas do Flutter, o contador.

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Contador',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  final String title;

  const MyHomePage({
    Key? key,
    required this.title,
  }) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headlineMedium,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Aqui temos uma estrutra simples, não vou explicar pois se você desenvolve em Flutter, só deve entrar nesse assunto de Gerenciamento de Estado mais profundo depois de ter entendido bem o próprio setState.

Pois é, se você não sabe o que é esse código acima, aconselho voltar um pouco para entender melhor a estrutura de Widgets do Flutter.

Então vamos lá. Podemos criar um controlador para gerenciar esse contador.

class Controller extends GetxController {
   
}

Note que na hora de fazer a importação, aparecerá várias formas de importar, isso acontece pois o get é desacoplado na questão de funcionalidades. veja na documentação:

GetX não está inchado. Ele possui uma infinidade de recursos que permitem começar a programar sem se preocupar com nada, mas cada um desses recursos fica em containers separados e só são iniciados após o uso. Se você usar apenas o gerenciamento de estado, apenas o gerenciamento de estado será compilado. Se você usar apenas rotas, nada do gerenciamento de estado será compilado.

Agora, vamos tirar a variável _counter e o método _incrementCounter da nosso _MyHomePageState.

Simplesmente precisamos movê-los para o nosso Controller, ficando assim:

class Controller extends GetxController {

  int counter = 0;
  void incrementCounter() {
    setState(() {
      counter++;
    });
  }
  
}

Pronto! A única mudança que fiz ai, foi tirar os "_" para poder acessar a variável e o método na View ( Claro que foi só para facilitar, mas teria como usar os getters e setters tranquilamente ).

Agora temos um problema! O setState depende de um State, e nós não temos mais esse State, a solução é muito simples, basta tirarmos ele e trocar por um update()

class Controller extends GetxController {

  int counter = 0;
  void incrementCounter() {
    counter++;
    update();
  }
  
}

Simples né? Agora, só falta mudar a View, vamos fazer isso agora.

// ...

class _MyHomePageState extends State<MyHomePage> {

  final controller = Get.put(Controller());
 
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            const Text(
              'You have pushed the button this many times:',
            ),
            GetBuilder<Controller>(
              builder: (_) {
                return Text(
                 '${controller.counter}',
                 style: Theme.of(context).textTheme.headlineMedium,
               );
              }
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: controller.incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ),
    );
  }
}

Agora podemos testar! Você verá o contador sendo incrementado perfeitamente.

Nós precisamos registrar o nosso Controller, eu fiz isso usando o Get.put, eu poderia usar o init do GetBuilder que também funcionaria.

Depois eu envolvi o Text em um GetBuilder que é o responsavel por reconstruir o Widget em uma atualização.

É importante tipar, GetBuilder< Seu tipo aqui >.

No builder dele, temos acesso a instância do próprio Controller, então poderiamos usar ele para acessar a variável ou o método.

Por fim, retornamos o Widget e com apenas isso, temos um gerenciamento de estados utilizando o GetX! :)

É realmente muito interessante o GetX, usei bastante num primeiro projeto que trabalhei com Flutter.

Só que entram daí problemas relacionados a manter toda a sua aplicação dependente de um Package né...

Adoro o jeito que o GetX faz as injeções de Dependências e Reatividade na UI.

Porém, o momento que pararem por exemplo de dar suporte ao Package, ou que você precisar modificar o projeto para outra ferramente, vai dar problema.

Ainda mais quando novos Devs entrarem, que vão ficar muito apegados ao Package, e acabando fechando as ideias criativas, por apenas terem conhecimento dele e quererem usar em tudo.

Não recomendo GetX para iniciantes. Acaba apenas dificultando o entendimento de como o Flutter funciona.

Concordo sobre a questão dos iniciantes e também de querer usar em tudo, porém sobre o suporte isso pode acontecer com qualquer outro package como o modular, que também eu vejo com muita frequência em vários projetos, dartz também usando o Either, entre outros... Sobre a questão do suporte, sempre existe essa possibilidade, e a dificuldade de remover o package, a não ser que você escreva tudo na mão. Onde eu trabalho ( [Strawti](https://www.instagram.com/straw.ti/) ), nós sempre optamos por usar o conjuto do GetX, porém não abrimos mão de projetos com mobx, modular, bloc, get_it e etc... Mas se tivermos a opção, vamos de GetX. "Não tem como deixar um projeto totalmente desacoplado, o próprio Flutter é um acoplamento." No caso você tem que escolher até que ponto será acoplado.