DEV Community

Lucien Risso Correia
Lucien Risso Correia

Posted on • Edited on

Gerência de Estado no Flutter

O ano era 2021 e a comunidade Flutter estava agitada demais com algumas tretas envolvendo o time do Flutter e desenvolvedores da comunidade, nessa mesma época o tal state management estava no hype de assuntos da comunidade. Fiz um texto originalmente no Medium, que também está publicado aqui, tirando sarro dessas polêmicas e satirizando os principais packages de gerenciamento de estado do Flutter.

Em projetos futuros a esse texto ainda continuou a discussão sobre qual package utilizar para isso, e a treta sempre foi grande. Chegamos em 2024 e sinto que isso deixou de ser um richa e finalmente os devs Flutter entenderam que no final tudo é Streams ou Observers, ou talvez não...


Observer

O Observer Pattern, também conhecido como Publish/Subscribe, é um padrão de design que define um mecanismo de comunicação entre objetos. Nesse padrão, um objeto, chamado Subject, mantém uma lista de objetos Observers que dependem dele. Quando o estado do Subject muda, ele notifica todos os Observers, que podem então atualizar suas próprias interfaces.

No Flutter esse designer é implementado no ChangeNotifier, onde se pode criar uma classe com as propriedades desejadas pro estado que quer gerenciar e métodos que manipulam e notificam a mudança de estado pros Observers. Veja um exemplo de uso básico de gerenciamento de estado com Observer em Flutter:

class MyState extends ChangeNotifier {
  int count = 0;

  void incrementCount() {
    count++;
    notifyListeners();
  }
}

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  MyState state = MyState();

  @override
  void initState() {
    super.initState();
    state.addListener(() {
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text('Count: ${state.count}');
  }
}
Enter fullscreen mode Exit fullscreen mode

Nesse exemplo, a classe MyState é um Subject. Ela define uma interface para registrar e remover Observers (o método addListener()) e notifica os Observers quando seu estado muda (o método notifyListeners()).

A classe MyWidget é um Observer. Ela se registra como um Observer da classe MyState no método initState(). Quando o estado da classe MyState muda, o método update() da classe MyWidget é chamado, o que faz com que o widget seja reconstruído.

Com o Provider podemos simplificar a parte de widget e retirar a parte de implementação do trecho do initState, já que o próprio widget dele já faz essa reconstrução quando recebe notificação que o estado mudou. Com isso temos pequenas mudanças no código de widget e ele ficaria assim:

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (_) => MyState(),
      child: Consumer<MyState>(
        builder: (context, state, child) {
          return Column(
            children: [
              Text('Count: ${state.count}'),
              ElevatedButton(
                onPressed: state.incrementCount,
                child: Text('Incrementar'),
              ),
            ],
          );
        },
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Lembre-se: o Provider tem como foco ser uma biblioteca para injeção de dependência utilizando a arvore de widgets, a classe Consumer auxilia no uso das dependências injetadas no contexto de Widget, o foco do Provider não é gerenciar estado.


Stream

Stream é uma sequência assíncrona de eventos. Ela pode ser usada para representar dados que mudam ao longo do tempo, como a localização do usuário, a entrada do teclado ou a saída de um sensor.

Um exemplo de uso se Stream para gerenciar estado no Flutter com um contador progressivo que incrementa o valor a cada segundo:

class MyState {
  int count = 0;

  Stream<int> get countStream =>
      Stream.periodic(const Duration(seconds: 1), (_) => count++);
}

class MyWidget extends StatelessWidget {
  final MyState state = MyState();

  @override
  Widget build(BuildContext context) {
    return StreamBuilder<int>(
      stream: state.countStream,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return Text('Count: ${snapshot.data}');
        } else {
          return Text('Aguardando...');
        }
      },
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Nesse exemplo usamos o StremBuilder para poder atualizar o widget a cada troca de valor, mas também podemos fazer sem ele com o famoso setState escutando os eventos da Stream com o listen:

class MyWidget extends StatefulWidget {
  @override
  _MyWidgetState createState() => _MyWidgetState();
}

class _MyWidgetState extends State<MyWidget> {
  final MyState state = MyState();

  @override
  void initState() {
    super.initState();
    state.countStream.listen((count) {
      setState(() {});
    });
  }

  @override
  Widget build(BuildContext context) {
    return Text('Count: ${state.count}');
  }
}
Enter fullscreen mode Exit fullscreen mode

E é sobre isso que venho lembrar e ajudar a fixar na cabecinha dos devs iniciantes em Flutter: quem gerencia o estado é você e não o package. O que quero lembrar é que independente de está usando BLoC ou MobX, Riverpod ou Solidart, GetX ou setState, o resultado final dependerá de como você escreveu a solução. E se não gostou de nenhuma implementação de Observer dos packages, crie sua própria, no final ou é feito em cima de Stream ou em cima de Observer.

Vou listar qual usa o quê pra exemplificar:

Packages que utilizam Stream:

  • BLoC
  • RxDart
  • GetX

Packages que utilizam Observer:

  • MobX
  • ChangeNotifier
  • Flutter Hooks

Por trás dos panos eles utilizam basicamente a mesma lógica, porém com implementações diferentes que mudam como será escrito o código. Mas igualmente, quem faz o gerenciamento é quem escreve a aplicação em si. Além disso o que ocorre muito é confundirem state managment com dependencie injection, no caso de GetX ou Provider por exemplo, que possuem essa implementação a mais para compor o package e tornar mais completo. São coisas que costumam serem necessitas juntamente porém já não são mais em si state managment.


Bom códigos e best reguards!

Top comments (0)