Skip to content
CrossFly is 100% open sourceFollow us on GitHub
CrossFlyCrossFly
Get started

This page was machine-translated from Portuguese and may contain errors. The Portuguese version is canonical.

How to centralize navigation in a controller

Purpose. Scattering new X().show() and hide() across screens couples the UI to the navigation graph. A controller concentrates push/pop, gives a single point for logging/guards, and enables “close down to the root”. It merely orchestrates the real EventsProcessor stack.

APIs used

  • Window.show() / Window.hide() — push/pop on the stack (the controller merely delegates to them).
  • EventsProcessor.topWindow() — static method that returns the top window (instance.windows.peek()).
  • EventsProcessor.instance.getLastWindow() — instance method; returns the real top of the stack (or null if empty). Use it when you need to check whether something is stacked without throwing an exception.

Names confirmed in the EventsProcessor source: topWindow() (static) and getLastWindow() (instance). topWindow() does windows.peek() and throws if the stack is empty; getLastWindow() returns null in that case — prefer this one for defensive checks.

Complete example (copy, paste and run)

package com.exemplo;

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

/** Runnable main screen: uses the Nav controller to push/pop screens. */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      add(new Label("Tela raiz"));
      add(new Button("Abrir detalhe").onAction(e -> Nav.push(new OutraTela())));  // push via controller
   }
}

/** Stacked screen: closes via controller (pop). */
class OutraTela extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      add(new Label("Detalhe"));
      add(new Button("Voltar").onAction(e -> Nav.pop()));  // pop via controller
   }
}

/** Controller: thin facade over the real EventsProcessor stack. */
final class Nav
{
   private Nav() {}

   /** push: stacks and displays. */
   public static void push(Window w)
   {
      w.show();
   }

   /** pop: closes the topmost window, if any. */
   public static void pop()
   {
      Window top = EventsProcessor.instance.getLastWindow();  // null-safe
      if (top != null)
         top.hide();
   }

   /** window currently on top (may be null). */
   public static Window current()
   {
      return EventsProcessor.instance.getLastWindow();
   }
}

The snippet below shows only the core. To run it, place it inside the MainWindow skeleton above.

Minimal snippet

// verified in the source: EventsProcessor.topWindow() (static) and instance.getLastWindow() (returns null if empty)
public final class Nav
{
   private Nav() {}

   /** push: stacks and displays. */
   public static void push(Window w)
   {
      w.show();
   }

   /** pop: closes the topmost window, if any. */
   public static void pop()
   {
      Window top = EventsProcessor.instance.getLastWindow();  // null-safe
      if (top != null)
         top.hide();
   }

   /** repeated pop until only 1 window remains (the root). */
   public static void popToRoot()
   {
      Window top;
      // while there is more than one window stacked, drop the top one
      while ((top = EventsProcessor.instance.getLastWindow()) != null
             && top != EventsProcessor.topWindow()) { top.hide(); }
      // (to "return to root", each hide() pops one; adjust the stopping criterion to your app)
   }

   /** window currently on top (may be null). */
   public static Window current()
   {
      return EventsProcessor.instance.getLastWindow();
   }
}

// usage in screens:
Nav.push(new DetalheTela());
// ...
Nav.pop();

Best practices:

  • Do not reimplement the stack: the EventsProcessor is already the source of truth. The controller is a thin facade over show()/hide().
  • Navigation guards (e.g., “are there unsaved changes?”) fit naturally here: check before pop() and, if needed, fire a confirmation Alert.
  • Be careful with topWindow() when the stack may be empty — it does peek() and throws. For defensive code use getLastWindow().

Go deeper

Was this page helpful?