DEV Community

Cover image for JavaFX: 3 Ways of Passing Information Between Scenes!
Miguel Manjarres
Miguel Manjarres

Posted on • Updated on

JavaFX: 3 Ways of Passing Information Between Scenes!

One of the most common issues that raises when developing a desktop application with JavaFX is "How can I pass this user input from scene A to scene B?", while you can always rely on text files or even databases, these may not be the most suitable approach.

The goal of this tutorial is to teach you three different ways you can pass information from one scene to another. Before we dive deep on that, let's first make a few assumptions:

  • We have a JavaFX app with two fxml files, SceneA and SceneB, each with its own controller, and we want to pass data from A to B.
  • We have a class that encapsulates the information we want. For the sake of this post, let's say this class is defined as follows: UML User Class Diagram
  • We have a method named sendData in the SceneAController where we gather the information. This method is called upon a mouseClicked event
  • We have a method named receiveData in the SceneBController where we populate an instance of the user class with the information we receive. This method is called upon a mouseClicked event

That's all 👍! Now about those methods...

First Method: Using the UserData function

In JavaFX, all the classes that inherit from the class Node have the following methods: setUserData(Object) and getUserData(). According to the official documentation, these methods allow us to attach an arbitrary object to a node for later use. I think you can see how powerful is that! Although we can use any node for this purpose, I recommend you use the stage itself, since it's the same no matter the scene.

In order to use this method, in the sendData function, follow these steps:

  1. Save the information in an instance of the user class
  2. Grab the node representing the button from the event object
  3. Get the instance of the stage from the node and close it
  4. Load the scene through the FXMLLoader class
  5. Pass the information to the stage using the setUserData function
  6. Create a new scene and pass it to the stage
  7. Make the stage visible again

The sendData function should look something like this:

In the receiveData function the only thing we have to do is:

  1. Get the instance of the stage just like before
  2. Recover the object using the getUserData function
  3. Use the information as you see fit

Hence, the receiveData function should look something like this:

And just like that we have managed to pass information between the two scenes, ¡Bravo 🎉!. This approach is very useful when we need a quick way to send data back and forth but, what if we need to pass more than one object? We will see how to overcome this in the next section.

Second Method: Manually Setting the Controllers

Usually when we create our fxml files, the IDE automatically creates a controller for it and link them together using the following property in the root tag of the fxml file:

fx:controller="<package>.Controller"
Enter fullscreen mode Exit fullscreen mode

In addition, this also creates a new instance of the controller class, but if we want to pass information to a field stored in the controller, like we would in a regular class, we must create the instance manually.

In order to use this method, we need to follow these steps:

  1. Remove the property we saw earlier from the SceneB.fxml file
  2. In the SceneBController, create a new attribute of type user along with its set and get function

Back in the sendData function:

  1. Follow the first three steps we saw in the previous section
  2. Create an FXMLLoader object and store in it the result of the statement FXMLLoader.load(...)
  3. Create a new instance of the SceneBController class and pass it the information using the set method you created
  4. Use the setController method in the FXMLLoader object you created and pass it the controller instance
  5. Use the load method and store its result in the root object, the rest is the same as before

The sendData function should look something like this:

Now in the receiveData function you can display the information stored in the user field, like this:

This method sure is more complex than the last one, but now you can pass as much objects as you like between the two scenes.

Third Method: Using a Singleton Class

Until now, we have relied on the controllers for passing the information, but if we truly want to mimic the business logic we would do if we were using a database for example, we can create a third class from which the two controllers can access. In order to do that, the controllers must share the same instance of the new class, and we can accomplish that using the Singleton pattern.

There are several ways we can implement this pattern, but the most simple one is by doing the following:

  1. Create a class and make its constructor private, so new instances can't be created
  2. Create a constant of the same type as the class and initialize it
  3. Create a public static function to retrieve said constant

In addition to these steps, you also need to create a field of the class type you are interested in, in this case is user, along with its set and get functions. For the sake of this tutorial, let's say we create a class named UserHolder and follow the steps, this would be the result:

Back in the sendData function:

  1. Follow the first 4 steps of the first section
  2. Grab the static instance of the UserHolder class
  3. Pass the information using the set function you created
  4. The rest is the same as before

And lastly in the receiveData function, you just need to recover the object from the static instance, like this:

And with a very few steps, we have managed to share information between the scenes through a singleton class. This approach is particularly useful when doing a configuration view, you can store in the singleton class the user's preferences and easily recover them anywhere in your program.

Final Thoughts

In this post, we learned how to pass information from a scene A to a scene B in three different ways, and we saw how we would implement this in our code as well as some use case where you might need them. I hope you have found this useful and want to thank you for your time.

Until next time 👋!

Top comments (7)

Collapse
 
uwais_dev profile image
Uwais

This was well written and explained, thanks @devtony101

Collapse
 
refeed profile image
Rafid Aslam

The step 2 on "Manually creating the controller" method section should be to create a new instance of FXMLLoader not calling FXMLLoader.load

FXMLLoader loader = new FXMLLoader(getClass().getClassLoader().getResource("fxml/SceneB.fxml"));
Enter fullscreen mode Exit fullscreen mode
Collapse
 
devtony101 profile image
Miguel Manjarres

Right, I can see why the title can be misleading, I will update it. Thank you for your feedback!

Collapse
 
shemuel2060 profile image
Samuel Katongole

I am new to javafx. I have a main controller and child controller, each having its own fxml file. The child fxml is displayed via a button on the main view, and it shows and waits till a button on it is clicked to submit data, then it closes; but the main one still exists. I am using a different stage for each, and I suppose it is why the first method does not work for my case. How would I have it work.

The logic is that once the button on the child view is clicked, the date entered in its inputs is displayed on the main view. How do I achieve this...

Collapse
 
pranavbarve125 profile image
pranavbarve125

I completely new to JavaFX and Java as well. Just for a thought, rather than going through any of this, why don't we have parent class for all the controller classes that we have in the application.
Is there anything problematic in doing that?

Collapse
 
dvargas135 profile image
Dan

Getting missing return type error in the last method. Perhaps you meant public static UserHolder getInstance()?

Collapse
 
hilmiarkan profile image
Maulana Hilmi Arkan

yup i think it is. Worked for me by doing that