Voltar para Home
por Eliezer Brasilian

Aprenda já a criar estado derivado em Java com o Megalodonte

Aprenda já a criar estado derivado em Java com o Megalodonte

Se você já viu o post anterior sobre o app contador, sabe que o State<T> do Megalodonte é o bloco básico de tudo: você muda o valor, a UI atualiza sozinha. Mas e quando você precisa de um valor que depende de outro estado? É aí que entra o ComputedState.

Neste exemplo prático, construí um app bem simples: um campo de texto onde o usuário digita algo, e logo abaixo aparece o tamanho (quantidade de caracteres) daquele texto, atualizado em tempo real a cada tecla pressionada.

O problema

Normalmente, em UIs imperativas, você precisaria:

  1. Escutar o evento de mudança do campo de texto;
  2. Pegar o novo valor;
  3. Calcular o tamanho;
  4. Atualizar manualmente o componente de texto na tela.

Com estado reativo, esses 4 passos somem. Você declara a relação entre os dados, e o framework cuida da propagação.

O código

Main.java — ponto de entrada padrão, define o tema e registra o listener de fechamento da aplicação:

package my_app;

import megalodonte.ListenerManager;
import megalodonte.application.MegalodonteApp;
import megalodonte.base.theme.ThemeManager;
import megalodonte.theme.DefaultTheme;

public class Main {

    static void main() {
        ThemeManager.setTheme(new DefaultTheme());

        MegalodonteApp.run(context -> context.useView(new HomeScreen()), ev->{
            if(ev == MegalodonteApp.Event.CloseRequest){
                System.out.println("Clicked on X - close application");
                ListenerManager.disposeAll();
            }
        });
    }
}

HomeScreen.java — onde a mágica do ComputedState acontece:

package my_app;

import megalodonte.ComputedState;
import megalodonte.base.components.Component;
import megalodonte.base.components.ScreenComponent;
import megalodonte.base.state.State;
import megalodonte.components.Text;
import megalodonte.components.inputs.Input;
import megalodonte.components.layout_components.Container;
import megalodonte.props.ContainerProps;

public class HomeScreen implements ScreenComponent {
    State<String> textState = new State<>("");
    ComputedState<String> textLenghtComputed = ComputedState.of(
            ()-> "Size is: " + textState.get().length(), textState
    );

    @Override
    public Component render() {
       return new Container(new ContainerProps().paddingAll(20)).children(
               new Input(textState),
               new Text(textLenghtComputed)
       );
    }
}

Como funciona, na prática

  • textState é um State<String> comum, exatamente como o State<Integer> do contador — só que guardando texto.
  • textLenghtComputed é criado com ComputedState.of(supplier, dependências...). O primeiro argumento é a lógica de cálculo (uma lambda), e o segundo é a lista de estados que, ao mudar, disparam o recálculo automático.
  • O Input(textState) já vem conectado diretamente ao estado — cada tecla digitada atualiza textState imediatamente.
  • O Text(textLenghtComputed) simplesmente exibe o valor computado, sem saber (nem precisar saber) de onde ele vem.

O resultado: digitar no campo atualiza o texto de tamanho instantaneamente, sem uma única linha de código de "atualização manual".

Por que isso importa

Esse padrão de ComputedState é a base para qualquer coisa mais complexa: validação de formulário em tempo real, contadores de caracteres restantes, campos calculados (tipo total de um carrinho de compras), filtros de lista baseados em texto digitado — tudo usa essa mesma ideia de "declarar a relação, deixar o framework propagar".


Publiquei também a versão desse post no Reddit, com mais contexto sobre o ecossistema Megalodonte:

Link do post no Reddit: https://www.reddit.com/r/JavaFX/s/6IU0PLkS71