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}');
}
}
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'),
),
],
);
},
),
);
}
}
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...');
}
},
);
}
}
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}');
}
}
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)