Navigation, persistence, theming, i18n, and UI utilities — wired together so you can ship screens, not boilerplate.
- What is CoreFx?
- Installation ⭐
- Quick Start
- The Modules
- Recipes
- Requirements
- Project Structure
- Building from Source
- Contributing
- License
Every JavaFX application re-implements the same plumbing: a singleton to switch screens, a place to stash the logged-in user, helpers to wire up tables, a way to theme scenes, and a validation toolkit. CoreFx is that plumbing, done once and done well.
It is a small, dependency-free library (JavaFX aside) that gives you:
| 🗺️ A navigation engine | One FlowController to load FXML views, swap scenes, open modals (blocking or not), and manage windows — with a cached FXMLLoader per view so controllers are reachable. |
| 🧠 Shared application state | A thread-safe AppContext key-value store for the current user, selected records, and feature flags — without coupling unrelated screens. |
| 🎨 Live theming | A ThemeManager that applies named CSS theme sets to any scene and re-applies them on the fly, tracking scenes with weak references so nothing leaks. |
| 🌍 Internationalization | Inject a ResourceBundle once and every FXML view loads localized — switch locale at runtime. |
| 🧰 Battle-tested utilities | Null-safe Validator, a standardized Answer response wrapper, type-safe TableUtils, Format input filters (Unicode-aware), alerts, image loading, and property binding. |
Each class is final, null-safe by contract, and documented. The runtime-free
classes ship with a 47-test JUnit suite.
CoreFx is published to Maven Central, so it works out of the box — no extra
repositories to configure. Use the latest version: 1.4.0.
<dependency>
<groupId>io.github.dinamo541</groupId>
<artifactId>corefx</artifactId>
<version>1.4.0</version>
</dependency>dependencies {
implementation 'io.github.dinamo541:corefx:1.4.0'
}dependencies {
implementation("io.github.dinamo541:corefx:1.4.0")
}CoreFx declares JavaFX with
providedscope — it does not bundle a JavaFX runtime. This is deliberate: your application controls which JavaFX version (and OS classifier) it ships with. You must add JavaFX yourself, or you will hitNoClassDefFoundError/ClassNotFoundExceptionat runtime.Target the same major version CoreFx is built against: JavaFX 25.
Maven — adding JavaFX
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>25</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>25</version>
</dependency>
</dependencies>Gradle — adding JavaFX (via the JavaFX plugin)
plugins {
id("org.openjfx.javafxplugin") version "0.1.0"
}
javafx {
version = "25"
modules = listOf("javafx.controls", "javafx.fxml")
}Module path users: CoreFx ships an
Automatic-Module-Nameofio.github.dinamo541.corefx, so it works on the JPMS module path without amodule-info.java.
A complete, minimal JavaFX application backed by CoreFx:
import io.github.dinamo541.corefx.navigation.FlowController;
import javafx.application.Application;
import javafx.stage.Stage;
public class MyApp extends Application {
@Override
public void start(Stage stage) {
FlowController flow = FlowController.getInstance();
flow.initialize(
stage, // the primary stage
"My Application", // window title
"/com/example/views/", // folder holding your .fxml files
"/com/example/resources/", // folder holding other resources
"/com/example/icon.png", // application icon
MyApp.class // class used to resolve resources
);
// Loads /com/example/views/Home.fxml, builds the scene,
// and shows the stage automatically.
flow.goViewMain("Home");
}
public static void main(String[] args) {
launch(args);
}
}From any controller, navigate without touching FXMLLoader again:
FlowController flow = FlowController.getInstance();
flow.goViewMain("Dashboard"); // swap the main scene's content
flow.goViewInModal("EditUser"); // open a non-blocking modal
flow.goViewInModalAndWait("Confirm");// open a blocking modal, wait for it to closeIf your app uses JPA, wire up EntityManagerHelper once at startup:
import io.github.dinamo541.corefx.persistence.EntityManagerHelper;
import jakarta.persistence.EntityManager;
import jakarta.persistence.Persistence;
// Once at startup — register the supplier (lazy, never called until first use):
EntityManagerHelper.getInstance().initialize(() ->
Persistence.createEntityManagerFactory("myUnit")
.createEntityManager());
// Anywhere afterwards — typed, cast-free retrieval:
EntityManager em = EntityManagerHelper.getInstance().getManager(EntityManager.class);
em.getTransaction().begin();
em.persist(entity);
em.getTransaction().commit();
// On application shutdown:
EntityManagerHelper.getInstance().close();CoreFx is organized into four packages under io.github.dinamo541.corefx.
io.github.dinamo541.corefx.navigation
| Class | Responsibility |
|---|---|
FlowController |
The heart of the library. Loads & caches FXML views, swaps scenes, opens windows and modals (blocking & non-blocking), swaps regions of a BorderPane, manages full-screen and min-size, and carries i18n + a typed transfer value. |
Controller |
Base class for all controllers in the application. Provides common functionality and lifecycle methods. |
AppContext |
Thread-safe, process-wide key-value store for shared application state. Backed by ConcurrentHashMap; keys must be non-blank, values non-null. |
StageManager |
Helpers for creating, configuring, and controlling JavaFX stages and windows. |
io.github.dinamo541.corefx.ui
| Class | Responsibility |
|---|---|
ThemeManager |
Register named CSS theme sets, activate one, and apply it to scenes. Live-switches all managed scenes; tracks them with weak references. |
AlertUtil |
Themed alert/confirmation dialogs (recommended for new code). |
Message |
Lightweight, theme-free alert/confirmation dialogs — a static utility. |
TableUtils |
Type-safe TableView setup: lambda-based columns, items, selection, placeholders, and a ready-made live search filter. |
Format |
TextFormatter input filters (integers, 2-decimal, IDs, Unicode-aware letters, max-length) plus shared date/decimal formatters. |
BindingUtils |
Two-way binding between a ToggleGroup and an ObjectProperty. |
ImageUtil |
Robust image loading from classpath resources, URLs, and local files. |
io.github.dinamo541.corefx.util
| Class | Responsibility |
|---|---|
Validator |
A null-safe predicate suite (isBlank, isEmail, isInRange, …) and throwing contract validators (requireNotBlank, requireInRange, …). Regex pre-compiled and linear-time. |
Answer |
An immutable-friendly response wrapper: success/failure state, user + internal messages, and a keyed result payload with a fluent builder. |
io.github.dinamo541.corefx.persistence
| Class | Responsibility |
|---|---|
EntityManagerHelper |
Simplified management of JPA/Hibernate EntityManager instances. |
Share state across screens with AppContext
AppContext ctx = AppContext.getInstance();
// After login:
ctx.put("currentUser", user);
// Anywhere else — the type is inferred:
User current = ctx.get("currentUser");
String role = ctx.getOrDefault("role", "guest");Apply and live-switch themes
ThemeManager themes = ThemeManager.getInstance();
themes.registerTheme("dark", "/app/css/dark.css");
themes.registerTheme("light", "/app/css/light.css");
themes.setActiveTheme("dark");
// Let every scene FlowController builds be themed automatically:
FlowController.getInstance().setThemeApplier(themes.asApplier());
// Flip the theme at runtime — all managed scenes update instantly:
themes.setActiveTheme("light");Localize your views (i18n)
ResourceBundle bundle = ResourceBundle.getBundle("i18n.messages", Locale.forLanguageTag("es"));
// Pass it at init time...
flow.initialize(stage, "Mi App", "/views/", "/res/", "/icon.png",
MyApp.class, themes.asApplier(), bundle);
// ...or switch the language later (clears the view cache for you):
flow.setIdioma(ResourceBundle.getBundle("i18n.messages", Locale.ENGLISH));FXML %key references resolve automatically.
Pass a value between views
// Sending screen:
flow.setTransferValue(selectedInvoice);
flow.goViewInModalAndWait("InvoiceDetail");
// Receiving controller:
Invoice invoice = flow.getTransferValue(Invoice.class);Validate input and return a structured result
Validator v = Validator.getInstance();
if (!v.isEmail(emailField.getText())) {
return Answer.failure("Please enter a valid e-mail address");
}
return Answer.success("Account created")
.with("userId", newUser.getId())
.with("user", newUser);Wire up a searchable table
TableUtils.addColumn(table, "Name", Person::getName);
TableUtils.addColumn(table, "Email", Person::getEmail);
ObservableList<Person> data = TableUtils.setItems(table, people);
TableUtils.installFilter(table, data, searchField,
(person, query) -> person.getName().toLowerCase().contains(query.toLowerCase()));Restrict a text field to Unicode letters
// Accepts "María", "Ñoño", "José" — rejects digits & symbols, max 50 chars:
nameField.setTextFormatter(Format.getInstance().lettersFormat(50));| Tool | Version |
|---|---|
| JDK | 25 |
| JavaFX | 25 (provided by your application) |
| Build tool | Maven or Gradle |
This repository is a Maven multi-module project. The published artifact is the
corefx core module.
CoreFx/
├── pom.xml # Parent POM — versions, plugins, metadata
├── .github/
│ ├── workflows/build.yml # CI: build + test on every push / PR
│ └── CONTRIBUTING.md
├── CHANGELOG.md
├── LICENSE # MIT
└── corefx/ # ← the published module
├── pom.xml
└── src/
├── main/java/io/github/dinamo541/corefx/
│ ├── navigation/ # FlowController, AppContext, StageManager
│ ├── persistence/ # EntityManagerHelper
│ ├── ui/ # ThemeManager, AlertUtil, Message, TableUtils, …
│ └── util/ # Validator, Answer
└── test/java/… # JUnit 5 suite (runtime-free classes)
# Clone
git clone https://github.com/Dinamo541/CoreFx.git
cd CoreFx
# Build, run tests, and install to your local Maven repo
mvn install
# Run just the tests
mvn -pl corefx testThe CI workflow runs mvn -B verify on Temurin JDK 25 for every push and pull
request to main.
Contributions are welcome! Please read the contribution guide first: 👉 CONTRIBUTING.md
For the full version history, see the 📝 CHANGELOG.md.
Released under the MIT License. See LICENSE for details.