Multi-Window Apps

IA Preparation: Most IA projects need more than one screen – a main view, an add/edit form, a details window. This page shows how to manage multiple windows and share data between them.

Table of Contents

  1. Why This Page Exists
  2. Approach 1: Scene Swapping (Single Stage)
    1. How It Works
    2. Switching Scenes from a Controller
  3. Approach 2: Popup Windows (Multiple Stages)
    1. Creating a Popup Window
    2. Key Concepts
    3. The Popup’s Controller
  4. Approach 3: Shared Model (Recommended for IA)
    1. The Pattern
  5. Passing Data to a Popup
  6. Complete Example: Student Manager with Add Popup
    1. StudentApp.java
    2. MainController.java
  7. Quick Check
  8. Code Completion
  9. Spot the Error
  10. Practice Exercises
    1. Core
    2. Extension
    3. Challenge
  11. Connections

Why This Page Exists

A real IA application typically has several screens:

  • A main screen showing a list or dashboard
  • An add/edit screen for entering or modifying data
  • A details screen for viewing a single record
  • Perhaps a settings or about screen

This page covers three approaches to multi-screen apps, from simplest to most flexible. Choose the one that fits your IA’s complexity.


Approach 1: Scene Swapping (Single Stage)

The simplest approach – load different FXML files into the same window by swapping the Scene.

How It Works

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class MyApp extends Application {
    private Stage primaryStage;

    @Override
    public void start(Stage stage) throws Exception {
        this.primaryStage = stage;
        showMainView();
        stage.setTitle("My App");
        stage.show();
    }

    public void showMainView() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("main-view.fxml")
        );
        Parent root = loader.load();

        MainController controller = loader.getController();
        controller.setApp(this);  // pass a reference so the controller can switch views

        primaryStage.setScene(new Scene(root, 500, 400));
    }

    public void showAddView() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("add-view.fxml")
        );
        Parent root = loader.load();

        AddController controller = loader.getController();
        controller.setApp(this);

        primaryStage.setScene(new Scene(root, 500, 400));
    }

    public static void main(String[] args) { launch(args); }
}

Switching Scenes from a Controller

public class MainController {
    private MyApp app;

    public void setApp(MyApp app) { this.app = app; }

    @FXML
    private void handleAddButton() throws Exception {
        app.showAddView();
    }
}

When to use: Simple apps with 2-3 screens where only one screen is visible at a time.

Limitation: The previous screen’s state is lost when you swap scenes (unless you save it to the Model first).


Approach 2: Popup Windows (Multiple Stages)

Open a separate window for add/edit forms while keeping the main window visible.

Creating a Popup Window

public class MainController {
    private StudentList model;

    @FXML
    private void handleAddButton() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("add-student-view.fxml")
        );
        Parent root = loader.load();

        AddStudentController addController = loader.getController();
        addController.setModel(model);

        Stage popup = new Stage();
        popup.setTitle("Add Student");
        popup.setScene(new Scene(root, 350, 250));
        popup.initModality(Modality.APPLICATION_MODAL);  // blocks the main window
        popup.showAndWait();

        // After popup closes, refresh the main display
        refreshDisplay();
    }
}

Key Concepts

Modality.APPLICATION_MODAL – The user must close this window before returning to the main window. This prevents the user from interacting with the main window while the popup is open. Use this for add/edit forms.

showAndWait() – Opens the window and pauses the calling code until the window is closed. This is what makes it possible to refresh the display after the popup closes.

show() – Opens the window but does not pause. The calling code continues immediately. Use this for non-blocking windows like “About” dialogs.

The Popup’s Controller

public class AddStudentController {
    private StudentList model;

    @FXML private TextField nameField;
    @FXML private TextField gradeField;
    @FXML private Label status;

    public void setModel(StudentList model) {
        this.model = model;
    }

    @FXML
    private void handleSave() {
        String name = nameField.getText().trim();
        String gradeText = gradeField.getText().trim();

        if (name.isEmpty()) {
            status.setText("Name cannot be empty.");
            return;
        }

        int grade;
        try {
            grade = Integer.parseInt(gradeText);
        } catch (NumberFormatException e) {
            status.setText("Grade must be a number.");
            return;
        }

        try {
            model.addStudent(new Student(name, grade));
            // Close this window
            nameField.getScene().getWindow().hide();
        } catch (IllegalArgumentException e) {
            status.setText(e.getMessage());
        }
    }

    @FXML
    private void handleCancel() {
        nameField.getScene().getWindow().hide();
    }
}

Closing a popup from its Controller: Use anyControl.getScene().getWindow().hide(). This gets the window that contains the control and hides it, which also releases the showAndWait() pause in the calling code.

When to use: Apps where the user needs to see the main screen while filling in a form, or where forms should feel like dialog boxes.


Both approaches above need a way to share data between controllers. The cleanest way is to pass the same Model instance to every controller.

The Pattern

┌─────────────────┐
│   Application    │  Creates the Model once
│   (wiring)       │  Passes it to every Controller
└────────┬────────┘
         │ passes model to
    ┌────┴────┐
    │         │
┌───▼───┐ ┌──▼────────────┐
│ Main  │ │ AddStudent    │
│ Ctrl  │ │ Controller    │
│       │ │               │
│ model ─┼─── model       │  Same object
└───────┘ └───────────────┘
// In the Application class:
StudentList model = new StudentList();  // ONE instance

// Pass to main controller:
MainController mainCtrl = loader.getController();
mainCtrl.setModel(model);

// When opening a popup:
AddStudentController addCtrl = popupLoader.getController();
addCtrl.setModel(model);  // SAME model instance

Because both controllers share the same StudentList object, when the popup adds a student, the main controller can see it immediately after the popup closes.

Never create a new Model in each Controller. Each controller would have its own separate data, and changes in one would not appear in the other.


Passing Data to a Popup

Sometimes you need to send specific data to a popup – for example, opening an “Edit Student” window pre-filled with the selected student’s information.

@FXML
private void handleEditButton() throws Exception {
    Student selected = getSelectedStudent();
    if (selected == null) {
        status.setText("Select a student first.");
        return;
    }

    FXMLLoader loader = new FXMLLoader(
        getClass().getResource("edit-student-view.fxml")
    );
    Parent root = loader.load();

    EditStudentController editCtrl = loader.getController();
    editCtrl.setStudent(selected);  // pass the specific student

    Stage popup = new Stage();
    popup.setTitle("Edit Student");
    popup.setScene(new Scene(root, 350, 250));
    popup.initModality(Modality.APPLICATION_MODAL);
    popup.showAndWait();

    refreshDisplay();
}
// In EditStudentController:
public void setStudent(Student student) {
    this.student = student;
    nameField.setText(student.getName());
    gradeField.setText(String.valueOf(student.getGrade()));
}

Complete Example: Student Manager with Add Popup

StudentApp.java

public class StudentApp extends Application {
    @Override
    public void start(Stage stage) throws Exception {
        StudentList model = new StudentList();

        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("main-view.fxml")
        );
        Parent root = loader.load();

        MainController controller = loader.getController();
        controller.setModel(model);

        stage.setTitle("Student Manager");
        stage.setScene(new Scene(root, 500, 450));
        stage.show();
    }

    public static void main(String[] args) { launch(args); }
}

MainController.java

public class MainController {
    private StudentList model;

    @FXML private TextArea display;
    @FXML private Label status;

    public void setModel(StudentList model) {
        this.model = model;
        refreshDisplay();
    }

    @FXML
    private void handleAdd() throws Exception {
        FXMLLoader loader = new FXMLLoader(
            getClass().getResource("add-student-view.fxml")
        );
        Parent root = loader.load();

        AddStudentController addCtrl = loader.getController();
        addCtrl.setModel(model);

        Stage popup = new Stage();
        popup.setTitle("Add Student");
        popup.setScene(new Scene(root, 350, 250));
        popup.initModality(Modality.APPLICATION_MODAL);
        popup.showAndWait();

        refreshDisplay();
        status.setText("Students: " + model.size());
    }

    private void refreshDisplay() {
        display.clear();
        for (int i = 0; i < model.getAll().size(); i++) {
            display.appendText((i + 1) + ". " + model.getAll().get(i) + "\n");
        }
    }
}

Quick Check

Q1. What does popup.initModality(Modality.APPLICATION_MODAL) do?

Q2. Why use showAndWait() instead of show() for a popup?

Q3. How should two controllers share data in an MVC application?

Q4. How do you close a popup window from inside its Controller?

Q5. When is scene swapping (single Stage) preferred over popup windows (multiple Stages)?


Code Completion

Complete the code that opens a popup window from a main controller.

Fill in the blanks: Complete the method that opens an "Add Student" popup.

@FXML
private void handleAdd() throws Exception {
    FXMLLoader loader = new FXMLLoader(
        getClass().getResource("add-student-view.fxml")
    );
    Parent root = loader.;

    AddStudentController ctrl = loader.;
    ctrl.setModel(model);

    Stage popup = new ;
    popup.setScene(new Scene(root, 350, 250));
    popup.initModality(Modality.APPLICATION_MODAL);
    popup.;

    refreshDisplay();
}

Spot the Error

This code opens a popup to add a student, but changes made in the popup do not appear in the main display.

Bug Hunt: Students added in the popup do not appear in the main window's display after the popup closes.

1FXMLLoader loader = new FXMLLoader( 2 getClass().getResource("add-view.fxml")); 3AddController ctrl = loader.getController(); 4ctrl.setModel(new StudentList()); 5Stage popup = new Stage(); 6popup.setScene(new Scene(loader.load())); 7popup.showAndWait(); 8refreshDisplay();

Pick the correct fix for line 4:


Practice Exercises

Core

  1. Add popup – Create a main window with a “Show Message” button. When clicked, it opens a modal popup with a label that says “Hello from the popup!” and a “Close” button that hides the popup.

  2. Scene swap – Create an app with two scenes: a “Home” scene with a “Go to Settings” button, and a “Settings” scene with a “Back to Home” button. Clicking either button swaps to the other scene in the same Stage.

Extension

  1. Edit popup – Extend the Student Manager: add an “Edit” button to the main view. When clicked, it opens a popup pre-filled with the selected student’s name and grade. The user can modify the values and save, updating the student in the shared Model.

  2. Confirm before close – Add a “close confirmation” to the main window: when the user clicks the window’s close button (X), show a confirmation Alert asking “Exit without saving?” with OK and Cancel buttons. Cancel should prevent the window from closing.

Challenge

  1. Multi-screen IA prototype – Design and build a 3-screen app for a simple inventory system: (1) main view listing all items, (2) “Add Item” popup with name, quantity, and price fields, (3) “Item Details” popup showing all info for a selected item. All screens share a single Inventory model. Use FXML for all views.

Connections


© EduCS.me — A resource hub for IB Computer Science

This site uses Just the Docs, a documentation theme for Jekyll.