Pular para o conteúdo
CrossFly é 100% open sourceSiga no GitHub
CrossFlyCrossFly
Começar

7. Gerenciando estado

O que você vai aprender: o modelo de estado do CrossFly — onde quem vem de Flutter/React precisa desaprender. Pré-requisitos: Lidando com entrada.

Exemplo completo (copiar, colar e rodar)

Uma ViewModel leve (TarefaVM) com observe(Runnable) manual (não há binding reativo): a View re-popula os componentes quando o estado muda. Um botão adiciona tarefas e a lista se re-renderiza. Compila como está.

package com.exemplo;

import java.util.*;
import dev.crossfly.ui.*;
import dev.crossfly.ui.layout.*;

public class MainWindow extends Window
{
   private final TarefaVM vm = new TarefaVM();
   private final Component listaBox = new Component();
   private int contador = 1;

   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Button add = new Button("Adicionar tarefa");
      add.onAction(c -> vm.adicionar(new Tarefa("Tarefa " + (contador++))));
      add(LAY_AUTO, add);

      listaBox.setLayout(new VerticalLayout());
      add(LAY_FILL, listaBox);

      vm.observe(this::renderizarLista);   // re-popula quando o estado muda
      renderizarLista();
   }

   private void renderizarLista()
   {
      listaBox.removeAll();
      for (Tarefa t : vm.tarefas) listaBox.add(LAY_AUTO, new Label(t.titulo));
      listaBox.setValidLayout(false);      // o tamanho mudou
      Component.repaint();
   }
}

class Tarefa
{
   final String titulo;
   Tarefa(String titulo) { this.titulo = titulo; }
}

class TarefaVM
{
   private Runnable onChange;
   public List<Tarefa> tarefas = new ArrayList<>();

   public void observe(Runnable r) { this.onChange = r; }
   private void mudou() { if (onChange != null) onChange.run(); }

   public void adicionar(Tarefa t)
   {
      var novo = new ArrayList<>(tarefas); novo.add(t);
      tarefas = novo;
      mudou();                             // notifica a View (na paint thread)
   }
}

Os trechos abaixo mostram só o miolo (o que vai dentro de onShow()). Para rodar, coloque-os no esqueleto MainWindow acima.

Não há binding reativo

Mudar o modelo não atualiza a UI sozinho. O estado vive em campos (ou numa ViewModel leve), e a View traduz estado → componentes manualmente, chamando repaint() (e setValidLayout(false) se o tamanho mudou).

Uma ViewModel leve com observe

public class TarefaVM {
   private Runnable onChange;
   public List<Tarefa> tarefas = List.of();
   public void observe(Runnable r) { this.onChange = r; }
   private void mudou() { if (onChange != null) onChange.run(); }

   public void adicionar(Tarefa t) {
      var novo = new ArrayList<>(tarefas); novo.add(t);
      tarefas = novo;
      mudou();                    // notifica a View (na paint thread)
   }
}

A View reage

@Override public void onShow() throws Exception {
   super.onShow();
   // … monta UI …
   vm.observe(this::renderizarLista);   // re-popula os componentes quando o estado muda
   vm.carregar();                       // dispara o carregamento (em background — próximo capítulo)
}

private void renderizarLista() {
   listaBox.removeAll();
   for (Tarefa t : vm.tarefas) { CardTarefa card = new CardTarefa(); listaBox.add(LAY_AUTO, card); card.bind(t); }
   listaBox.setValidLayout(false);
   Component.repaint();
}

Regra: mutações e notificações sempre na paint thread. Se o dado veio de fundo, entre em Component.onUI(...) antes de mexer no estado que a View lê (ver próximo capítulo).

Constrói neste passo

A TarefaVM com a tela se re-renderizando quando o estado muda.

Recapitulando

Sem reativo: estado em campos/ViewModel; a View traduz estado→componentes com repaint(); um observe(Runnable) manual substitui o binding.

Próximos passos

→ Carregando dados

Veja também

Esta página foi útil?