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 show a spinner and a progress bar (Progress)

Purpose. Indicate that something is in progress. Progress has three modes, chosen by the constructor: spinning icon (spinner), determinate bar (0–100% with a known value) and indeterminate (infinite loop, when the time is unknown). Use determinate when you have a known fraction (upload, download); indeterminate/spinner for “loading…” with no fraction.

APIs used

  • new Progress(Icon icon) — Spinning mode: rotates the icon continuously.
  • new Progress(float min, float max) — Determinate bar mode: creates the bar with the range [min, max]; initial value = min.
  • progress.setValue(float v) — updates the value (clamped to [min, max]); recomputes the filled fraction. Fluent.
  • new Progress(ProgressType type) — Indeterminate mode: infinite bar/spinner in the given style.
  • ProgressType.MATERIAL, ProgressType.FLUTTER, ProgressType.IOS, ProgressType.STRIPED — visual variants of the indeterminate mode (IOS is a circular spinner; the others are bars).
  • progress.pivot(int px, int py) — (spinning mode) custom rotation pivot; -1,-1 = center of the icon.

Non-obvious behavior (confirmed in source): the animation starts by itself when the Progress is added to a parent (onAdd) and stops when it is removed (onRemove). There is no public start()/stop() — to “hide” the progress, remove the component from the screen. The mode field is final (set in the constructor, immutable).

Complete example (copy, paste and run)

package com.exemplo;

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

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      // 1) Spinner (spinning icon) — generic "loading"
      Progress spinner = new Progress(new MaterialIcon("autorenew", 48));
      add(spinner);

      // 2) Determinate bar (0 to 100) — update as real progress happens
      Progress barra = new Progress(0f, 100f);
      add(barra);
      // ...as the download advances:
      barra.setValue(75f);   // 75% filled

      // 3) Indeterminate (infinite loop) — with no known fraction
      Progress infinito = new Progress(ProgressType.MATERIAL);
      add(infinito);

      // iOS-style circular spinner variant:
      Progress ios = new Progress(ProgressType.IOS);
      add(ios);
   }
}

The snippet below shows just the core. To run it, place it in the MainWindow skeleton above.

Minimal snippet

// 1) Spinner (spinning icon) — generic "loading"
Progress spinner = new Progress(new MaterialIcon("autorenew", 48));
add(spinner);

// 2) Determinate bar (0 to 100) — update as real progress happens
Progress barra = new Progress(0f, 100f);
add(barra);
// ...as the download advances:
barra.setValue(75f);   // 75% filled

// 3) Indeterminate (infinite loop) — with no known fraction
Progress infinito = new Progress(ProgressType.MATERIAL);
add(infinito);

// iOS-style circular spinner variant:
Progress ios = new Progress(ProgressType.IOS);
add(ios);

Verification note (2026 code). Confirmed in source: the three constructors Progress(Icon), Progress(float min, float max) and Progress(ProgressType), and the enum Progress.Mode { SPINNING_ICON, DETERMINATE_BAR, INDETERMINATE } derived from them. setValue clamps to [min, max] and updates progress (fraction 0..1). The ProgressType lives in UIConstants with the values IOS, FLUTTER, MATERIAL, STRIPED. In a test environment (DeviceConfig.isTesting) the animation does not start automatically.

Dig deeper

Was this page helpful?