A página abaixo é funcional, mas há alguns problemas na infraestruturas que podem ser melhorados. Mas primeiro a descrição: a árvore dde Widgets abaixo é do tipo StatefulWidget, ou seja, a tela é redesenhada dinamicamente de acordo com que as variáveis declaradas dentro da classe de estado do Widget mudam, mas para isso, é necessário chamar a função setState, que chama novamente a função build, assim as variáveis alteradas são visíveis na tela.
Para início de papo, devemos esclarecer que a função createType deve ser declarada dentro da classe de estado, assim teremos acesso as variáveis de estado.
Mas vamos ao que interessa:
Separando responsabilidades:
A função createType cria uma caiaxa de diálogo ( e consequentemente um novo contexto), mas ela permanece modificando o estado da tela principal de forma direta, isso não é uma boa prática, pois fica um emaranhado de estados e contextos, e para os menos experiêntes, fica a dúvida, esse setState pertece a qual árvore?
TextButton( child: const Text("Salvar"), onPressed: () { selectedIcon ??= Icons.credit_score; types.add(ClientType(name: nomeInput.text, icon: selectedIcon)); selectedIcon = null; setState(() {}); Navigator.pop(context); } ),
A melhor forma de fazer isso é separar a atualização de estados, cada árvore atualiza seu próprio estado, podemos fazer com que a função createType retorne um tipo
Future<ClientTypeModel> createType(context) {...}
E esperamos esse tipo dentro do contexto da tela principal:
floatingActionButton: FloatingActionButton( child: Icon(Icons.add), onPressed: () async { ClientTypeModel result = await createType(context); setState(() { clientTypes.add(result); }); }, ),
E dentro do contexto da tela principal devemos adicionar esse novo objeto na lista. Assim não cometemos o erro de dificultar a leitura do nosso próprio código.
Observação: O Flutter opera com um modelo baseado em single thread, o que significa que devemos ter cautela ao construir caixas de diálogo. Em algumas situações, as funções responsáveis pelo retorno podem não se comportar como esperado, resultando em valores inesperados ou até mesmo null.
Tive essa experiência ao refatorar a função iconPicker. Ao aplicar o princípio de separação de responsabilidades, percebi um grande problema: iconPicker retornava null porque não aguardava corretamente a finalização de showDialog. O problema acontecia porque showDialog, ao concluir sua execução, modificava a variável selectedIcon, mas a função iconPicker já havia retornado antes dessa atualização.
Após explorar a documentação do Core de Dart, encontrei uma solução eficiente: a classe Completer. Essa classe permite que funções assíncronas (Future) retornem valores de forma controlada, garantindo que o resultado só seja disponibilizado após a conclusão da ação necessária.
Com essa abordagem, obtive o seguinte resultado: ao pressionar o botão, iconPicker não retornava mais null, pois sua execução esperava explicitamente que showDialog finalizasse antes de retornar um IconData?.
Falta de atualização de estado
- O bloco abaixo não atualiza o estado corretamente:
StatefulBuilder(builder: (BuildContext context, StateSetter setState) { return Column(children: [ const Padding(padding: EdgeInsets.all(5)), selectedIcon != null ? Icon(selectedIcon, color: Colors.deepOrange) : const Text('Selecione um ícone'), const Padding(padding: EdgeInsets.all(5)), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () async { final IconData? result = await showIconPicker(context: context, defalutIcon: selectedIcon); setState(() { selectedIcon = result; }); }, child: const Text('Selecionar icone') ), ), ]); }),
- O bloco acima cria um contexto separado, no qual não há modificadores de estado. Como consequência, o estado da caixa de diálogo não é atualizado corretamente. Para evitar esse problema, a abordagem mais prática é aplicar o conceito descrito acima e envolver toda a caixa de diálogo com um StatefulBuilder. Dessa forma, ao pressionar o botão responsável por escolher o ícone, conseguimos atualizar o estado de toda a caixa de diálogo, garantindo que as mudanças sejam refletidas imediatamente na interface.