Sample Task: Creating a “Dashboard” View with FXML and Various Layout Containers
We will create a simple “Dashboard” view that demonstrates how different Layout Containers can be combined to build a complex interface. Each section of the dashboard will use a different Layout Container.
1. Create a new JavaFX project in IntelliJ IDEA
2. Create a new package and classes
Create a new package bg.tu_varna.sit.ps.sample_task. Inside it, create the classes DashboardApplication, Dashboard, and Launcher.
-
src/main/java/bg/tu_varna/sit/ps/sample_task/DashboardApplication.java(Main Class): This class will start the application and load our FXML file.package bg.tu_varna.sit.ps.sample_task; import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; import java.io.IOException; public class DashboardApplication extends Application { @Override public void start(Stage stage) throws IOException { // Load our FXML file FXMLLoader fxmlLoader = new FXMLLoader(DashboardApplication.class.getResource("dashboard-view.fxml")); Parent root = fxmlLoader.load(); new Dashboard(root); Scene scene = new Scene(root, 900, 650); stage.setTitle("Dashboard Application"); // Window title stage.setScene(scene); // Set the scene graph for the window stage.show(); // Show the window } } -
src/main/java/bg/tu_varna/sit/ps/sample_task/Dashboard.javaThis class will serve to add interactivity to our FXML file. At the moment, we won’t add logic, but it is necessary for linking the FXML with the Java code.package bg.tu_varna.sit.ps.sample_task; import javafx.scene.Parent; import javafx.scene.control.Label; public class Dashboard { private final Label headerLabel; private final Label navigationLabel; public Dashboard(Parent root) { headerLabel = (Label) root.lookup("#headerLabel"); navigationLabel = (Label) root.lookup("#navigationLabel"); init(); } private void init() { headerLabel.setText("Dashboard"); navigationLabel.setText("Home > Dashboard"); } } -
src/main/java/bg/tu_varna/sit/ps/sample_task/Launcher.java(Startup Class): This class contains the main method to start the application.package bg.tu_varna.sit.ps.sample_task; import javafx.application.Application; public class Launcher { public static void main(String[] args) { Application.launch(DashboardApplication.class, args); } }
3. Create the FXML file for the Dashboard view
Create a new directory resources/bg/tu_varna/sit/ps/sample_task/ and inside it create an FXML file named dashboard-view.fxml.
-
src/main/resources/bg/tu_varna/sit/ps/sample_task/dashboard-view.fxml(FXML file): This is where we define our user interface using a combination of Layout Containers.<?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.*?> <?import javafx.scene.layout.*?> <?import javafx.scene.text.Font?> <BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="850.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1"> <top> <HBox alignment="CENTER" prefHeight="50.0" BorderPane.alignment="CENTER"> <Label text="Dashboard Header" fx:id="headerLabel"> <font> <Font name="System Bold" size="24.0"/> </font> </Label> </HBox> </top> <left> <VBox prefWidth="150.0" spacing="10.0" style="-fx-background-color: #E0E0E0; -fx-padding: 10;" BorderPane.alignment="CENTER"> <Label text="Navigation" fx:id="navigationLabel"/> <Button maxWidth="1.7976931348623157E308" text="Section 1"/> <Button maxWidth="1.7976931348623157E308" text="Section 2"/> <ToggleButton fx:id="statusToggleButton" maxWidth="1.7976931348623157E308" text="Status"/> <Region VBox.vgrow="ALWAYS"/> <Button maxWidth="1.7976931348623157E308" text="Settings"/> </VBox> </left> <center> <StackPane BorderPane.alignment="CENTER" style="-fx-padding: 10;"> <!-- Welcome VBox --> <VBox alignment="CENTER" spacing="20.0" visible="true"> <!-- visible="true" by default --> <Label text="Welcome to your Dashboard!"/> <Label text="Select a section from the left."/> </VBox> </StackPane> </center> <bottom> <HBox alignment="CENTER_LEFT" prefHeight="30.0" style="-fx-background-color: #F0F0F0; -fx-padding: 5;" BorderPane.alignment="CENTER"> <Label text="Status: Ready"/> <Region HBox.hgrow="ALWAYS"/> <Label text="Version 1.0"/> </HBox> </bottom> <right> <FlowPane hgap="5.0" vgap="5.0" prefWidth="150.0" orientation="VERTICAL" style="-fx-background-color: #D0D0D0; -fx-padding: 10;" BorderPane.alignment="CENTER"> <Label text="Quick Links"/> <Button text="Link A"/> <Button text="Link B"/> <Button text="Link C"/> <Button text="Link D"/> <Button text="Link E"/> </FlowPane> </right> </BorderPane>
Explanations of the FXML file:
BorderPane(root): We useBorderPaneas the main Layout Container, as it is ideal for the general structure of an application with a header (top), footer (bottom), side navigation (left/right), and central content.- The
topregion contains anHBoxfor the title. - The
leftregion contains aVBoxfor the navigation menu. - The
centerregion contains aStackPanefor the main content (currently just a greeting). - The
bottomregion contains anotherHBoxfor the status bar. - The
rightregion contains aFlowPanefor quick links.
- The
HBox(Horizontal Box): Used intopandbottomfor horizontal arrangement of elements. Notealignment="CENTER"andalignment="CENTER_LEFT".Region HBox.hgrow="ALWAYS"is used as a separator that occupies all remaining space, pushing other elements to the edges.VBox(Vertical Box): Used inleftfor vertical arrangement of navigation buttons.spacing="10.0"adds a 10px distance between buttons.Region VBox.vgrow="ALWAYS"is used to push the “Settings” button to the very bottom.StackPane(Stacking Container): Used incenter. Currently it has only oneVBoxin it, but if we add more elements, they will stack on top of each other in the center.FlowPane(Flow Container): Used in therightregion for “Quick Links”. Withorientation="VERTICAL", the buttons are arranged vertically and “overflow” to a new column if there isn’t enough space.hgapandvgapset the distances between elements.
4. Adding GridPane and AnchorPane to the center of the StackPane
To demonstrate the other Layout Containers, we will change the content of the StackPane in the central region. Imagine we want to show two different sections: one with a form (GridPane) and one with information (AnchorPane), which could be swapped. For the purposes of this exercise, we will make them visible simultaneously, but they will reside within the StackPane.
You can replace the current VBox in the StackPane with the following code:
<!-- Welcome VBox -->
<VBox alignment="CENTER" spacing="20.0" visible="true"> <!-- visible="true" by default -->
<Label text="Welcome to your Dashboard!"/>
<Label text="Select a section from the left."/>
</VBox>
<!-- Example GridPane: User Data Form -->
<GridPane alignment="CENTER" hgap="10.0" vgap="10.0" style="-fx-background-color: #FFFFFF; -fx-padding: 20;"
StackPane.alignment="CENTER"
visible="false"> <!-- Change to visible="true" to see it -->
<columnConstraints>
<ColumnConstraints minWidth="100.0" prefWidth="120.0"/>
<ColumnConstraints minWidth="200.0" prefWidth="250.0"/>
</columnConstraints>
<Label text="User Registration" GridPane.columnSpan="2"
style="-fx-font-weight: bold; -fx-font-size: 16px;"/>
<Label text="Name:" GridPane.rowIndex="1"/>
<TextField promptText="Enter name" GridPane.columnIndex="1" GridPane.rowIndex="1"/>
<Label text="Password:" GridPane.rowIndex="2"/>
<PasswordField promptText="Enter password" GridPane.columnIndex="1" GridPane.rowIndex="2"/>
<Label text="Profile Type:" GridPane.rowIndex="3"/>
<HBox spacing="10.0" alignment="CENTER_LEFT" GridPane.columnIndex="1" GridPane.rowIndex="3">
<fx:define>
<ToggleGroup fx:id="accountGroup"/>
</fx:define>
<RadioButton fx:id="personalRadio" text="Personal" toggleGroup="$accountGroup"/>
<RadioButton text="Business" toggleGroup="$accountGroup"/>
</HBox>
<Label text="Subscription:" GridPane.rowIndex="4"/>
<CheckBox fx:id="newsletterCheckBox" text="Subscribe to newsletter" GridPane.columnIndex="1"
GridPane.rowIndex="4"/>
<Label text="Notes:" GridPane.rowIndex="5" GridPane.valignment="TOP"/>
<TextArea prefHeight="80.0" promptText="Enter additional info..." wrapText="true"
GridPane.columnIndex="1" GridPane.rowIndex="5"/>
<Button maxWidth="Infinity" text="Submit" GridPane.columnIndex="1" GridPane.rowIndex="6"
defaultButton="true"/>
</GridPane>
<!-- Example AnchorPane: Detailed Information -->
<AnchorPane style="-fx-background-color: #F8F8F8; -fx-padding: 20;" StackPane.alignment="CENTER"
visible="false"> <!-- Change to visible="true" to see it -->
<Label layoutX="14.0" layoutY="14.0" text="Detailed Information" AnchorPane.leftAnchor="10.0"
AnchorPane.topAnchor="10.0">
<font>
<Font name="System Bold" size="18.0"/>
</font>
</Label>
<TextArea editable="false" layoutX="14.0" layoutY="40.0" prefHeight="150.0" prefWidth="300.0"
text="This is a detailed information section. It can contain various types of content, images, or longer texts."
wrapText="true" AnchorPane.bottomAnchor="50.0" AnchorPane.leftAnchor="10.0"
AnchorPane.rightAnchor="10.0" AnchorPane.topAnchor="50.0"/>
<Button layoutX="273.0" layoutY="210.0" mnemonicParsing="false" text="Close"
AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="10.0"/>
</AnchorPane>
Explanations for the added graphical elements:
GridPane(Grid Container):columnConstraints(column constraints) androwConstraints(row constraints) are defined to manage column width and row height.- Each control is positioned using
GridPane.columnIndexandGridPane.rowIndex. GridPane.columnSpanallows the “Submit” button to occupy two columns.
AnchorPane(Anchoring Container):- Uses
AnchorPane.topAnchor,AnchorPane.bottomAnchor,AnchorPane.leftAnchor,AnchorPane.rightAnchorto anchor controls to the sides of the container. This ensures adaptive positioning relative to the size of theAnchorPane. - The
Labelis anchored top-left, theTextAreais anchored on all sides (with an offset), and theButtonis anchored bottom-right.
- Uses
visible="false": In the example,visible="false"is set for theGridPaneandAnchorPaneso that only the greeting is visible initially. To see them, change tovisible="true"for the respective control. In a real application, this would be managed by Java code based on navigation selection.
5. Running the Application
Run Launcher.java (Right click -> Run 'Launcher.main()'). You should see the application window with all defined Layout Containers. Experiment with the window size to see how the Layout Containers react to changes.