Dashboard con Gráficas Animadas y Navegación en JavaFX
Construye un panel de administración moderno en JavaFX. Aprende a darle estilo oscuro a las Charts por defecto y a usar Timeline para dibujar gráficas de forma progresiva y fluida.
JJ Arroyo
3 de marzo de 2026 • 10 min de lectura

Un Panel de Control o "Dashboard" es posiblemente uno de los requerimientos más comunes en aplicaciones empresariales e internas. Los clientes quieren ver datos resumidos, gráficas y una forma fácil de navegar entre reportes.
Por defecto, los componentes LineChart y BarChart de JavaFX son un poco... "antiguos". Fondos completamente blancos, líneas grises delgadas y ejes con mucho ruido visual.
En este tutorial, construiremos un Dashboard Animado Completo que cuenta con:
- Sidebar de Navegación moderna usando un layout
BorderPane. - Rediseño CSS de Gráficas pasando a un entorno "Premium Dark Mode".
- Data-Staggering (Animaciones con Timeline) para que, en lugar de que los datos aparezcan todos de golpe al cargar la vista, las gráficas crezcan fluida y secuencialmente de izquierda a derecha.
El Esqueleto: BorderPane y StackPane (FXML)
Un buen Dashboard usa un BorderPane. Pondremos los botones en la región left (para crear el sidebar) y luego en el center pondremos todo nuestro contenido.
Para intercalar entre diferentes "Vistas" (Ej. Overview vs Analytics) sin cargar múltiples Scene, usamos un truco muy útil: Todo el contenido va dentro de un StackPane, y ocultamos (visible="false") o mostramos los contenedores internos dependiendo de qué botón presiones en el sidebar.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.chart.BarChart?>
<?import javafx.scene.chart.CategoryAxis?>
<?import javafx.scene.chart.LineChart?>
<?import javafx.scene.chart.NumberAxis?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Circle?>
<BorderPane styleClass="root-pane" stylesheets="@../css/style.css" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.dashboard_animado.controller.DashboardController">
<!-- Sidebar de Navegación -->
<left>
<VBox spacing="15" styleClass="sidebar">
<padding><Insets top="30" right="20" bottom="30" left="20" /></padding>
<HBox alignment="CENTER_LEFT" spacing="10" styleClass="brand-container">
<Circle radius="15" styleClass="brand-logo" />
<Label text="Nexus UI" styleClass="brand-text" />
</HBox>
<VBox spacing="10" VBox.vgrow="ALWAYS">
<padding><Insets top="40" /></padding>
<Button fx:id="btnOverview" text="Overview" styleClass="nav-btn, nav-btn-active" onAction="#showOverview" maxWidth="Infinity" alignment="BASELINE_LEFT" />
<Button fx:id="btnAnalytics" text="Analytics" styleClass="nav-btn" onAction="#showAnalytics" maxWidth="Infinity" alignment="BASELINE_LEFT" />
<Button fx:id="btnReports" text="Reports" styleClass="nav-btn" onAction="#showReports" maxWidth="Infinity" alignment="BASELINE_LEFT" />
</VBox>
</VBox>
</left>
<!-- Contenido Central -->
<center>
<VBox>
<!-- Header Superior -->
<HBox alignment="CENTER_RIGHT" styleClass="header" spacing="20">
<padding><Insets top="20" right="30" bottom="20" left="30" /></padding>
<Label fx:id="lblSectionTitle" text="Overview" styleClass="section-title" HBox.hgrow="ALWAYS" maxWidth="Infinity" />
<HBox alignment="CENTER" spacing="10">
<Label text="Admin User" styleClass="user-name" />
<Circle radius="18" styleClass="user-avatar" />
</HBox>
</HBox>
<!-- Workspace: Intercambiador de Vistas con StackPane -->
<StackPane fx:id="contentArea" VBox.vgrow="ALWAYS">
<padding><Insets top="30" right="30" bottom="30" left="30" /></padding>
<!-- Vista 1: Actividad de Ventas (BarChart) -->
<VBox fx:id="viewOverview" spacing="20" styleClass="card">
<padding><Insets top="20" right="20" bottom="20" left="20" /></padding>
<Label text="Sales Activity" styleClass="card-title" />
<BarChart fx:id="barChart" legendVisible="false" VBox.vgrow="ALWAYS" animated="false">
<xAxis><CategoryAxis fx:id="barXAxis" styleClass="axis-label" /></xAxis>
<yAxis><NumberAxis fx:id="barYAxis" styleClass="axis-label" /></yAxis>
</BarChart>
</VBox>
<!-- Vista 2: Crecimiento de Usuarios (LineChart) -->
<VBox fx:id="viewAnalytics" spacing="20" styleClass="card" visible="false">
<padding><Insets top="20" right="20" bottom="20" left="20" /></padding>
<Label text="User Growth" styleClass="card-title" />
<LineChart fx:id="lineChart" legendVisible="false" VBox.vgrow="ALWAYS" createSymbols="true" animated="false">
<xAxis><CategoryAxis fx:id="lineXAxis" styleClass="axis-label" /></xAxis>
<yAxis><NumberAxis fx:id="lineYAxis" styleClass="axis-label" /></yAxis>
</LineChart>
</VBox>
</StackPane>
</VBox>
</center>
</BorderPane>
[!CAUTION] Nota que establecimos
animated="false"en la configuración del XML para las Charts. Esto es intencional. La animación por defecto de JavaFX a veces da fallos visuales o hace cosas extrañas (como superponer líneas durante el dibujado). Desactivarla permite que nosotros mismos escribamos nuestro propio motor de dibujado.
Hackeando la apariencia de JavaFX Charts (CSS)
La maravilla de JavaFX es que te expone sub-propiedades exclusivas para los gráficos de su toolkit a través de CSS. Aquí logramos un par de propósitos:
- Quitar el fondo blanco horrendo de la pizarra (
chart-plot-background). - Ocultar las líneas horizontales e interiores molestas.
- Usar un gradiente colorido y vibrante para las gráficas.
/* style.css - Fragmento relevante para gráficas de JavaFX */
.chart {
-fx-padding: 10px;
}
/* 1. Remover el fondo blanco del panel de trazado */
.chart-plot-background {
-fx-background-color: transparent;
}
.chart-content {
-fx-background-color: transparent;
}
/* 2. Suavizar los textos de los ejes */
.axis {
-fx-tick-label-fill: #9ca3af;
-fx-font-size: 11px;
}
.axis-tick-mark,
.axis-minor-tick-mark {
-fx-stroke: transparent;
}
.axis-label {
-fx-text-fill: #9ca3af;
}
/* 3. Limpiar las líneas horizontales cuadrículadas */
.chart-vertical-grid-lines {
-fx-stroke: transparent;
}
.chart-horizontal-grid-lines {
-fx-stroke: #374151;
-fx-stroke-dash-array: 5 5;
}
/* 4. Colores Premium (Líneas y Puntos) */
.chart-series-line {
-fx-stroke: #3b82f6; /* Azul brillante estilo Neón */
-fx-stroke-width: 3px;
}
.chart-line-symbol {
-fx-background-color: #111827, #3b82f6;
-fx-background-insets: 0, 2;
-fx-background-radius: 5px;
-fx-padding: 5px;
}
/* 5. Gradientes vivos para las Barras */
.default-color0.chart-bar {
-fx-background-color: linear-gradient(
to top,
#8b5cf6,
#c084fc
); /* Gradiente Púrpura */
-fx-background-radius: 6px 6px 0 0;
}
Animación en Cascada (Staggering) con Timeline
Cuando cargamos un Dashboard queremos el "efecto de construcción visual", donde las gráficas se van dibujando solas apenas hacemos clic en el tab del menú de nuestra izquierda.
Dado que le dijimos a la gráfica por defecto que no sea animada (animated="false"), el truco es iterar la data, usar KeyFrame de javafx.animation multiplicando el index temporal, para que cada dato que metemos al Series de la tabla se haga un poco más tarde que el dato anterior.
// Lógica interna del controlador (DashboardController)
// ...
private void switchView(VBox viewToShow) {
viewOverview.setVisible(false);
viewAnalytics.setVisible(false);
viewReports.setVisible(false);
viewToShow.setVisible(true); // Solo mostrar la activa
}
private void animateBarChart() {
barChart.getData().clear();
XYChart.Series<String, Number> series = new XYChart.Series<>();
barChart.getData().add(series);
String[] months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"};
Timeline timeline = new Timeline();
// Agregar "Data points" progresivamente con Delay basado en milisegundos iterativos
for (int i = 0; i < months.length; i++) {
final String month = months[i];
final int value = 200 + random.nextInt(800);
KeyFrame kf = new KeyFrame(Duration.millis(i * 150), e -> {
series.getData().add(new XYChart.Data<>(month, value));
});
timeline.getKeyFrames().add(kf);
}
timeline.play();
}
private void animateLineChart() {
lineChart.getData().clear();
XYChart.Series<String, Number> series = new XYChart.Series<>();
lineChart.getData().add(series);
Timeline timeline = new Timeline();
int baseUsers = 1000;
// Dibuja la línea fluida usando 100ms de retraso entre cada nodo
for (int i = 1; i <= 15; i++) {
final String day = "Day " + i;
baseUsers += random.nextInt(250) - 50;
final int users = baseUsers;
KeyFrame kf = new KeyFrame(Duration.millis(i * 100), e -> {
series.getData().add(new XYChart.Data<>(day, users));
});
timeline.getKeyFrames().add(kf);
}
timeline.play();
}
Descarga el Proyecto Formato Premium
Te hemos dejado este "Boilerplate" o código de inicio completo de una manera accesible para que puedas adaptarlo a MySQL, PostgreSQL, APIs, o donde sea que leas la data de tu aplicación real y conectarla a tu JavaFX.
Las viejas vistas de Java por defecto han pasado a la historia cuando combinas gradientes CSS e ingenuidad con Timelines.