JavaFX FXML/window switcher I would like to write simple application where I'm going to make a few views:


Login
Register
Main app


I think that my method is not the best way to do this. Can someone give me a tip?

Main.java

package application;

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


public class Main extends Application {
    @Override
    public void start(Stage primaryStage) {
        try {
            BorderPane root = (BorderPane)FXMLLoader.load(getClass().getResource("Login.fxml"));
            Scene scene = new Scene(root,400,400);
            scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

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


Controller.java

package application;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class Controller implements Initializable{

    @FXML
    Button openRegister;

    @FXML
    Button openLogin;

    private Stage stage;

    @Override
    public void initialize(URL location, ResourceBundle resources) {

    }

    @FXML
    private void openRegister() throws IOException{
        stage = (Stage) openRegister.getScene().getWindow();
        BorderPane root;
        root = (BorderPane) FXMLLoader.load(getClass().getResource("Register.fxml"));
        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stage.setScene(scene);
        System.out.println("Register.fxml opened");
    }

    @FXML
    private void openLogin() throws IOException{
        stage = (Stage) openLogin.getScene().getWindow();
        BorderPane root;
        root = (BorderPane) FXMLLoader.load(getClass().getResource("Login.fxml"));
        Scene scene = new Scene(root);
        scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
        stage.setScene(scene);

        System.out.println("Login.fxml opened");
    }

}


Login.fxml



    &lt;?xml version="1.0" encoding="UTF-8"?&gt;

    &lt;?import java.lang.*?&gt;
    &lt;?import javafx.scene.control.*?&gt;
    &lt;?import javafx.scene.layout.*?&gt;
    &lt;?import javafx.scene.layout.BorderPane?&gt;

    &lt;BorderPane xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller"&gt;
       &lt;center&gt;
          &lt;Button fx:id="openRegister" mnemonicParsing="false" onMouseClicked="#openRegister" text="Button" BorderPane.alignment="CENTER" /&gt;
       &lt;/center&gt;
    &lt;/BorderPane&gt;


Register.fxml

&lt;?xml version="1.0" encoding="UTF-8"?&gt;

        &lt;?import java.lang.*?&gt;
        &lt;?import javafx.scene.control.*?&gt;
        &lt;?import javafx.scene.layout.*?&gt;
        &lt;?import javafx.scene.layout.BorderPane?&gt;

        &lt;BorderPane xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller"&gt;
           &lt;center&gt;
              &lt;Button fx:id="openLogin" mnemonicParsing="false" onMouseClicked="#openLogin" text="Button" BorderPane.alignment="CENTER" /&gt;
           &lt;/center&gt;
        &lt;/BorderPane&gt;
I think you are on the right track, there are only a few things I would change.


I would create a static function in the controller which inflates and returns a root node to be added to the stage (or even launches a new stage).
I would make a separate controller for each window/fragment you want to create. So you would have a LoginController and a RegisterController, each inflating and binding to their respective fxml files. 


Depending on how your app is going to to be structured, you'll likely want a MainController which provides a container to swap sub-controllers in and out of. In this way, each controller is a single entity that can be used ad-hoc; only the main controller will be aware of their existence and be responsible for displaying them. MainController would also provide the ideal place for global menus, titles, toolbar, tabs, etc.



UPDATE

public static LoginUiController initScene(Stage stage) throws IOException
{
    LoginUiController controller = new LoginUiController();

    // Inflate FXML
    FXMLLoader loader = new FXMLLoader(LoginUiController.class.getResource("controller/login/Login.fxml"));
    loader.setController(controller);
    controller.root = loader.load(); // Good to have a pointer to the root node so the controller can be nested

    // Create scene             
    Scene scene = new Scene(controller.root);                                    
    stage.setScene(scene);
    controller.onCreated(); // Method to let the controller know it has been inflated and added to a scene

    return controller;
}


I generally make use of an abstract base controller that exposes the different methods I use (inflateFxml, initScene, onCreate) as well as a property binder lookup table, so the controller can unbind all listeners when closed.