Value Notifier com Pattern State
No ultimo post sobre ValueNotifier falamos de forma mais abstrata, por isso, nesse post irei detalha mais sobre ValueNotifier e State Pattern juntos. Vamos la?
Vamos recapitular rapidamente. As sintaxes mais utilizadas para a criação do ValueNotifier são as seguintes:
class TodoController {
final todoNotifier = ValueNotifier<List<TodoModel>>([])
//final anyThingNotifierHere = ValueNotifier<AnyThing>([])
Future<void> loadTodos() {
todoNotifier.value = []; // Todo models adicionados aqui
}
}
Ou
class TodoController extends ValueNotifier<List<TodoModel>> {
TodoController(): super([]);
Future<void> loadTodos() {
value = [] // Todo models adicionados aqui
}
}
No entanto, neste post, utilizaremos mais essa segunda abordagem. Então, vamos lá.
Gosto muito da explicação do guru sobre o padrão State, mas vou resumir de forma sucinta aqui. O padrão State nada mais é do que definir os diferentes momentos da aplicação. Como assim? Vou explicar com mais detalhes abaixo. Por exemplo, temos uma tela que carrega “Todos”, e nessa tela podemos determinar que ela terá diferentes momentos/estados:
- START -> Este é o momento inicial da nossa tela.
- LOADING -> Este é o momento de carregamento (geralmente colocamos um CircularProgressIndicator ou algo semelhante).
- LOADED -> Aqui o carregamento (LOADING) terminou e já temos os nossos “Todos”.
- ERROR -> Aqui o carregamento (LOADING) terminou, mas ocorreu algum problema.
Aqui está traduzido para o código:
sealed class TodoState {}
//Estado START
class TodoStateStart extends TodoState {}
// Estado LOADING
class TodoStateLoading extends TodoState {
}
// Estado de LOADED
class TodoStateLoaded extends TodoState {
final List<TodoModel> todos;
TodoStateLoading({
required this.todos,
});
}
// Estado de ERROR
class TodoStateError extends TodoState {
final String message;
TodoStateError({
required message,
});
}
Vamos utilizar isso em conjunto com ValueNotifier:
class TodoController extends ValueNotifier<TodoState> {
Todocontroller(): super(TodoStateStart());
Future<void> loadTodos() {
try {
value = TodoStateLoading();
// requiscao à alguma fonte de dados aqui
value = TodoStateLoaded(todos: [TodoModel(title: "Teste")]);
} on ErroTal catch (erro) {
value = TodoStateError(message: erro.message);
}
}
}
Você pode observar que, no ValueNotifier, aplicamos o conceito de polimorfismo na prática utilizando o tipo base TodoState. Dessa forma, podemos usar diferentes formas que estendem a classe base, como TodoStateLoading, TodoStateLoaded, entre outras.
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget>
with SingleTickerProviderStateMixin {
final todoController = TodoController();
@override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: todoController,
builder: (BuildContext context, value, Widget? child) {
final state = value;
if (state is TodoStateLoading) {
return const CircularProgressIndicator();
}
if (state is TodoStateError) {
return Text(state.message);
}
if (state is TodoStateSuccess) {
return ListView.builder(
itemCount: state.todos.length,
itemBuilder: (BuildContext context, int index) {
final todo = state.todos[index];
return ListTile(
title: Text(todo.title),
);
},
);
}
},
);
}
}
Como você pode ver acima, podemos usar o padrão State para controlar o que será mostrado em cada estado. Na minha modesta opinião, isso é muito bonito e funcional.
Você pode ter acesso a mais conteúdo gratuito e de qualidade como este entrando na comunidade FlutterBrasil, para uma melhor imersão no mundo do Flutter. Espero que tenha gostado. Meu nome é Edson, do Flutter Brasil, e te vejo no próximo artigo. Até logo!