Interview at FX Experience

JavaOne is coming closer and closer. On the one hand I can’t wait to be there but on the other hand I need to finish all my talks in only a few days fearful
At canoo we prepared a short overview of all our talks. If you fare interested in a preview of the canoo talks watch them.
In addition a interview with me about JavaOne and my current work was posted at FX Experience and another overview about my talks can be found at the JavaOne blog.
I think that I won’t have time to add an additional blog post before JavaOne. But at the moment I have several post finished that describes new APIs and tricks that I want to show at JavaOne :) I you can’t be there you should stay tuned and wait for these posts.

Back to all the slides… Here are some previews of my work today (DataFX & Extreme Gui Makeover):
sneak peak
sneak peek 2

JavaOne Preview: Enterprise JavaFX

Today I want to give a short preview of one of my talks at JavaOne: Enterprise JavaFX.
In this talk I will discuss different problems in enterprise development with JavaFX. The talk will introduce some solutions and best practice how to handle background threads, call web services or make use of Java EE specifications like dependency injection. In addition some frameworks will be shown.

If you are in this topics meet me at Java One :)
Venue / Room: Hilton – Plaza A
Date and Time: 9/29/14, 16:00 – 17:00

Because I think a good talk must contain cool demos, I created some examples especially for my talks. Today I will introduce one of them with a short video. The video shows how you can login with Twitter in JavaFX. You can use this as an alternative workflow for users to login or register in your application. If you want to know how this is done you should visit my “Enterprise JavaFX” talk :)

JavaOne 2014 Preview

As I mentioned last week I will give 6 talks at JavaOne this year. To get a better overview of this talks I recorded a short video in that I introduce the talks and show a short preview of some JavaFX demos:

Most of the talks will be hold with other speakers. Here is a list of all the people I’m currently working on cool stuff for JavaOne:

My JavaOne 2014 Sessions

Uh, as a featured speaker I will give a lot sessions this year at JavaOne. This is the main reason why I found no time to blog about JavaFX the last weeks ;)
If you will attend JavaOne I will be happy to see you at one of my session:

Enterprise JavaFX
Venue / Room: Hilton – Plaza A
Date and Time: 9/29/14, 16:00 – 17:00

DataFX: From External Data to a UI Flow and Back
Venue / Room: Hilton – Plaza B
Date and Time: 9/29/14, 11:00 – 12:00

Smart UIs for Mobile and Embedded in JavaFX
Venue / Room: Hilton – Plaza B
Date and Time: 9/30/14, 19:00 – 19:45

The JavaFX Community and Ecosystem
Venue / Room: Hilton – Plaza B
Date and Time: 9/30/14, 11:00 – 12:00

Extreme GUI Makeover
Venue / Room: Hilton – Yosemite B/C
Date and Time: 10/1/14, 13:00 – 14:00

Test-Driven Development with JavaFX
Venue / Room: Hilton – Plaza B
Date and Time: 10/1/14, 11:30 – 12:30

Maybe I can organize some “Mastering JavaFX 8 Controls” books for the audience :)

DataFX Tutorial 5

This is the last tutorial about the basic flow and action API of DataFX. I plan to start a second tutorial series about injection and resources in DataFX. But in those parts of DataFX 8 the APIs aren’t final at the moment. So it will sadly take one or two month before I can continue the tutorials. An overview of all tutorials can be found here.

In this tutorial I want to show how flows can be interlaced. Therefore we create a new variation of the wizard app that was shown in tutorial 4. The application should provide a simple wizard with some steps. Here is an overview how the dialogs and the flow where defined in tutorial 4:
flow3
Once the application is finished it will look like the own that was created last time:
views
The last implementation of the wizard has one weak point in its architecture: Each step of the wizard is defined as a view in a flow. The view contains the complete view of the application and so also the action toolbar is part of each view. In the last tutorial we extracted the toolbar in a separate FXML file but the controls were created for each view individually. But the toolbar has the same functionality on each view and normally it should be one single instance and only the view in the center of the application should change:
tut5-2
This more logical structure is part of this tutorial and by using interlaced flows the favored behavior can be created. As always we will start by creating the FXML files for the application. Because the top views of the wizard should be defined in a separate flow there will be FXML files for the views that only contain the content of one wizard step. Here is an example for the first step:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>

<Label text="Welcome!">
    <font>
        <Font size="24.0"/>
    </font>
</Label>

As you can see the FXML is much shorter than in the other tutorials. It only contains the Label that will be shown on the first view of the wizard.
For the toolbar we can use the same FXML file as in tutorial 4:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<HBox alignment="CENTER_RIGHT" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="12.0" style="-fx-background-color: darkgray;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
  <Button fx:id="backButton" mnemonicParsing="false" text="Back" />
  <Button fx:id="nextButton" mnemonicParsing="false" text="Next" />
  <Button fx:id="finishButton" mnemonicParsing="false" text="Finish" />
</children>
<padding>
  <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
</padding>
</HBox>

The FXML defines a HBox that include the 3 action buttons.
Once this is done the application is defined by the following FXML files:
tut5-3
As a next step a master FXML file is needed that includes the different components. As shown in then last tutorial the fx:include tag can be used to include FXML files within each other. Because the toolbar will be static component and the bottom of the application it can be included in the main FXML file. The wizard views on the top will change. Therefore we add a StackPane to the main FXML file that will hold the views. Here is the FXML file:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.StackPane?>
<?import javafx.scene.text.Font?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<BorderPane prefHeight="240.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <bottom>
        <fx:include source="actionBar.fxml"/>
    </bottom>
    <center>
        <StackPane fx:id="centerPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
        </StackPane>
    </center>
</BorderPane>

The StackPane that will hold the central views of the wizard is defined by the unique id “centerPane”. Once this is done all FXML files are finished and we can start to code in JavaFX. Let’s take a look at the general structure of the application before we start to define the needed flows. As said the application will contain two flows that are interlaced. Here is a graphic that shows the structure:
tut5-1
We will start with the view controller classes for all the wizard steps. Because the action toolbars isn’t part of this views anymore and they contain only a label the code is very short :) Here is the class for the first view controller:

@FXMLController("wizardView1.fxml")
public class WizardView1Controller {
}

Easy, isn’t it? The class is only needed to specify the view in the Java code and create the binding between the class and the FXML file. As you can see in the code of the example all other controller classes will look like this.
As a next step we will create the flow of for these views. Here we only need to link the view for navigation. Therefore the definition of the flow will look like this:

Flow flow = new Flow(WizardView1Controller.class).
                withLink(WizardView1Controller.class, "next", WizardView2Controller.class).
                withLink(WizardView2Controller.class, "next", WizardView3Controller.class).
                withLink(WizardView3Controller.class, "next", WizardView4Controller.class).
                withLink(WizardView4Controller.class, "next", WizardView5Controller.class);

Ok, but where should this flow be defined. Until now all flows were defined in the main class of the application but this flow is an inner flow that will be part of another flow. To better understand how this can be achieved we should have a look at the main class of the application:

public class Tutorial5Main  extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        Flow flow  = new Flow(WizardController.class);

        FlowHandler flowHandler = flow.createHandler();

        StackPane pane = flowHandler.start(new DefaultFlowContainer());
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();
    }
}

As in all other tutorials a flow is defined and started in the application class. But as you can see this flow defines only one start view that is defined by its controller class (WizardController). This class defines the controller of the main view of the application that is defined by the main FXML file as already shown. The flow that defines the steps of the wizard will be an inner flow in this view. Therefore the inner flow is defined in the WizardController controller class:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    private StackPane centerPane;

    private FlowHandler flowHandler;

    @PostConstruct
    public void init() throws FlowException {
        Flow innerFlow = new Flow(WizardView1Controller.class).
                withLink(WizardView1Controller.class, "next", WizardView2Controller.class).
                withLink(WizardView2Controller.class, "next", WizardView3Controller.class).
                withLink(WizardView3Controller.class, "next", WizardView4Controller.class).
                withLink(WizardView4Controller.class, "next", WizardView5Controller.class);

        flowHandler = innerFlow.createHandler();
        centerPane.getChildren().add(flowHandler.start(new AnimatedFlowContainer(Duration.millis(320), ContainerAnimations.ZOOM_IN)));
    }
}

Here all the steps of the wizard are added and linked to the inner flow. As you can see in the code the flow is started in a difference way than it was done several times before. A FlowHandler instance is created and this instances is started. This is wrapped in the startInStage() method of the Flow class that was used in all the other tutorials. The FlowHandler instance represents a running flow. The Flow class only defines the structure of a flow and can be started several times in parallel. Each running flow instance is defined by a FlowHandler instance and The FlowHandler instance can be used to interact with the flow. In addition the start(…) method of the FlowHandler class needs a FlowContainer as parameter. The FlowContainer provides a parent node in that the flow will be shown. In this case we use an AnimatedFlowContainer that provides animations whenever the flow navigates to a different view. We will see the animations at the end of the tutorial. The flow will be placed in the centerPane that is defined by FXML and injected in the controller by using the @FXML annotation.
As said the FlowHandler class provides methods to interact with the flow. We will use this methods in the tutorial to call the “next”, “back” and “finish” actions of the flow. To do so we need the action buttons that can easily be injected in the controller class:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;
    @FXML
    @ActionTrigger("finish")
    private Button finishButton;
    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    @FXML
    private StackPane centerPane;

    //init()...
}

All the Buttons are annotated by the @ActionTrigger annotation and will therefore trigger actions in the flow. But they won’t trigger actions in the inner flow because they are defined in the global flow that is defined in the main class. To handle this actions we will create some methods that are annoted by the @ActionMethod() annotation. As seen in tutorial 1 these methods will be called whenever the action with the matching unique id is called:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;

    @FXML
    @ActionTrigger("finish")
    private Button finishButton;

    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    //init()...

    @ActionMethod("back")
    public void onBack() throws VetoException, FlowException {
    }

    @ActionMethod("next")
    public void onNext() throws VetoException, FlowException {
    }

    @ActionMethod("finish")
    public void onFinish() throws VetoException, FlowException {
    }
}

In these methods the FlowHandler instance of the inner flow can be used to interact with the inner flow and navigate to new views or trigger actions. In addition some logic is needed to enable and disable the action buttons depending on the state of the inner flow. Here is the complete code of the WizardController class with the final implementation of these methods:

@FXMLController("wizard.fxml")
public class WizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;

    @FXML
    @ActionTrigger("finish")
    private Button finishButton;

    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    @FXML
    private StackPane centerPane;

    private FlowHandler flowHandler;

    @PostConstruct
    public void init() throws FlowException {
        Flow flow = new Flow(WizardView1Controller.class).
                withLink(WizardView1Controller.class, "next", WizardView2Controller.class).
                withLink(WizardView2Controller.class, "next", WizardView3Controller.class).
                withLink(WizardView3Controller.class, "next", WizardView4Controller.class).
                withLink(WizardView4Controller.class, "next", WizardView5Controller.class);

        flowHandler = flow.createHandler();
        centerPane.getChildren().add(flowHandler.start(new AnimatedFlowContainer(Duration.millis(320), ContainerAnimations.ZOOM_IN)));

        backButton.setDisable(true);
    }

    @ActionMethod("back")
    public void onBack() throws VetoException, FlowException {
        flowHandler.navigateBack();
        if(flowHandler.getCurrentViewControllerClass().equals(WizardView1Controller.class)) {
            backButton.setDisable(true);
        } else {
            backButton.setDisable(false);
        }
        finishButton.setDisable(false);
        nextButton.setDisable(false);
    }

    @ActionMethod("next")
    public void onNext() throws VetoException, FlowException {
        flowHandler.handle("next");
        if(flowHandler.getCurrentViewControllerClass().equals(WizardView5Controller.class)) {
            nextButton.setDisable(true);
            finishButton.setDisable(true);
        } else {
            nextButton.setDisable(false);
        }
        backButton.setDisable(false);
    }

    @ActionMethod("finish")
    public void onFinish() throws VetoException, FlowException {
        flowHandler.navigateTo(WizardView5Controller.class);
        finishButton.setDisable(true);
        nextButton.setDisable(true);
        backButton.setDisable(false);
    }
}

Once this is done you can start the flow and navigate through all the steps of the wizard. Thanks to the AnimatedFlowContainer container each link will be animated:

As I said this is the final tutorial about the Flow API. From our point of view the API is finshed and we only plan to polish some stuff and add documentation. But we plan to release DataFX 8 at JavaOne and so there is still some time to change things. So if you have any questions, feedback or improvements please let us now. The next step is the CDI part of DataFX and once the APIs are stable I will continue this tutorial series.

AeroFX: It’s getting closer

As shown in the sneak peek last week a new JavaFX theme is in development. Matthias Meidinger is creating this skin as part of his bachelor thesis and since the last week he made some big improvements. So I think it’s time for a new preview of the upcoming JavaFX theme. The following pics show dialogs that use the AeroFX skin:

j1
j5

Currently all the animations for focused controls and custom SVG parts like the check mark for checkboxes are in development. Once this parts are done I will post a new update. We hope you like this work ;)

Sneak Peek: AeroFX

Last year we released AquaFX as a JavaFX 8 theme that emulates a native Look for JavaFX applications on Mac OS. Applications that use AquaFX may look like this:
AquaFX-specials
A complete documentation of AquaFX can be found on its project page and at our blog.

Today I want to show a sneak peek on a new project called AeroFX. AeroFX is a theme for JavaFX that will emulate the native look of the Windows interface. AeroFX will use the same architecture as AquaFX so that developers can easily switch between the skins or support both on different operation systems. The skin of AeroFX will look perfect on Windows 7. At the moment AeroFX is in a very early stage and contains only skins for some basic controls. Because AeroFX is developed as a bachelor thesis the source code can’t be opened at the moment. But once the work is finished AeroFX will be provided under the same open source license as AquaFX. Here is the first screenshot of AeroFX:

aeroFX sneak peek

DataFX Tutorial 4

This is the 4th tutorial about navigation with DataFX and the DataFX flow API. An overview of all tutorials can be found here. In this tutorial I will show how you can manage the action handling and navigation of a flow from outside of the flow. To do so we will refactor the wizard that was created in tutorial 3.

As described in tutorial 3 the wizard will be composed of some views that define the steps of the wizard. In addition a toolbar with some buttons is placed on the bottom. The views will look like this:
views
All views of the wizard are linked by a navigation model. In tutorial 3 this was created by directly in the view controller classes so each view defines its navigation and actions. In this tutorial we will use the second approach that DataFX provides: All views doesn’t know anything about there action and navigation model. Instead of this the actions are defined extern. The navigation and action behavior will be the same as in tutorial 3. Here is a short overview about the links between the views of the wizard:
flow3
As always we want to start by defining the views in FXML. Because the toolbar will look the same on each view we can extract it in a seperate FXML file. As shown in tutorial 3 a FXML file can included in another one by using the fx:include tag. Here is the FXML definition of the toolbar:

<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<HBox alignment="CENTER_RIGHT" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minHeight="-Infinity" minWidth="-Infinity" prefHeight="-1.0" prefWidth="-1.0" spacing="12.0" style="-fx-background-color: darkgray;" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
  <Button fx:id="backButton" mnemonicParsing="false" text="Back" />
  <Button fx:id="nextButton" mnemonicParsing="false" text="Next" />
  <Button fx:id="finishButton" mnemonicParsing="false" text="Finish" />
</children>
<padding>
  <Insets bottom="12.0" left="12.0" right="12.0" top="12.0" />
</padding>
</HBox>

The definition of the toolbar is the same as in the last tutorial. The definition of the wizard steps is the same, too. Here is a FXML definition of one step:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.text.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<BorderPane prefHeight="240.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
      <bottom>
          <fx:include source="actionBar.fxml" />
      </bottom>
      <center>
          <StackPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
              <children>
                  <Label text="Welcome!">
                      <StackPane.margin>
                          <Insets bottom="32.0" left="32.0" right="32.0" top="32.0" />
                      </StackPane.margin>
                      <font>
                          <Font size="24.0" />
                      </font>
                  </Label>
              </children>
          </StackPane>
      </center>
</BorderPane>

As a next step we need view controller classes for all views in the wizard. As a first step we will create empty classes that are annoted with the FXMLController annotation:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller {

}

All the actions of the wizard will be triggered by the toolbar. Because the toolbar is defined on each view we can create an abstract class for the toolbar components that can be used as a superclass for the view controller classes:

public class AbstractWizardController {

    @FXML
    @ActionTrigger("back")
    private Button backButton;

    @FXML
    @ActionTrigger("finish")
    private Button finishButton;

    @FXML
    @ActionTrigger("next")
    private Button nextButton;

    public Button getBackButton() {
        return backButton;
    }

    public Button getFinishButton() {
        return finishButton;
    }

    public Button getNextButton() {
        return nextButton;
    }
}

All view controller classes can now extend the class:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller extends AbstractWizardController {

}

Mainly the same structure was created in tutorial 3 but here we have one big different. In Chapter 3 the next button can’t be defined in the super class because it was annotated with the @LinkAction annotation. This must be done in each controller class because the target of the navigation must be defined as an parameter of the annotation definition (see tutorial 3 for a more detailed description). As already mentioned we want to extract the action handling in this tutorial. So all buttons can be defined and injected in the AbstractWizardController class. As you can see in the code the buttons are annoted by the @ActionTrigger annotation that was already used in some other tutorials. By using this annotation an action id is defined and the flow will trigger the action that is specified by this id each time the button will be pressed.

The last thing that is missing is the main class. This class will look different to the main classes of the first three tutorials. Here we want to define all the actions of the wizard and link its views. To do so we start with a simple class and show the flow. You should know this kind of class from the other tutorials:

public class Tutorial4Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(WizardStartController.class).startInStage(primaryStage);
    }
}

As a next step we want to bind all the different views and create a navigation model. To do so the Flow class contains some methods that can be used to define links between views of the flow. The Flow class is defined as a fluent API and navigation links or action can be simply added to it. Here is a example for a flow with one link:

new Flow(View1Controller.class).
withLink(View1Controller.class, "link-id", View2Controller.class).
startInStage(primaryStage);

In the flow a link from view 1 to view 2 is defined. Both views are specified by their controller classes (View1Controller.class and View2Controller.class) and the link is defined by the unique id “link-id”. Once this is done you can simply add the @ActionTrigger(“link-id”) annotation to a defined node in the View1Controller class. DataFX will register the navigation to view 2 for this node. So whenever the node is clicked the navigation will be transformed.
For the current example the code of the main class will look like this:

public class Tutorial4Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        new Flow(WizardStartController.class).
                withLink(WizardStartController.class, "next", Wizard1Controller.class).
                withLink(Wizard1Controller.class, "next", Wizard2Controller.class).
                withLink(Wizard2Controller.class, "next", Wizard3Controller.class).
                withLink(Wizard3Controller.class, "next", WizardDoneController.class).
                withGlobalBackAction("back").
                withGlobalLink("finish", WizardDoneController.class).
                startInStage(primaryStage);
    }
}

Next to the withLink(…) method two additional methods of the Flow class are used in the code. The withGlobalLink(…) method defines a navigation action that will be registered for each view in the flow. So the @ActionTrigger(“finish”) annotation can be used in each view and will navigate to the last view of the wizard. For each action type that can be registered to a DataFX flow the Flow class provides methods to register the action for only one view or as a global action for all views. This is done for the back action, too. The “back” button is visible on each view of the wizard and therefore the withGlobalBackAction(“back”) method is used here. So whenever a action with the id “back” is triggered in any view of the flow a back action will be called. This is exactly the same as adding a @BackAction annotation to the node that should trigger the back action.
All these methods add a action to the DataFX flow. A action is defined by the interface org.datafx.controller.flow.action.FlowAction and all the shown methods will internally call the Flow.addActionToView(Class controllerClass, String actionId, FlowAction action) method that will add a action instance for the defined id to a specific view. Methods that add global actions will call Flow.addGlobalAction(String actionId, FlowAction action) internally. As you can see even custom actions can be added to a flow by simply implementing the FlowAction interface. DataFX contains a set with all the most important actions types that can be added to a flow or a specific view. The following figure shows the inheritance of the FlowAction interface:
ACTION-uml
Some of the actions in the diagram will be handled in future tutorials. First I will only explain the basic action types:

  • FlowLink defines a link to another view in the flow. In the example instances of this class will be registered to the flow whenever the withLink(…) method is called.
  • FlowBackAction handles a back navigation in the flow. This is the same as using the @BackAction annotation in a view controller
  • FlowTaskAction will execute a task that is defined as a Runnable on the Platform Application Thread. We will see an example of the action type after this overview.
  • FlowAsyncTaskAction will execute a task that is defined as a Runnable on a background thread.
  • FlowMethodAction will call a method in the given view. The same result can be created by using the @ActionMethod annotation as shown in the first tutorial.

As you can see in this overview all the actions that were created by using annotations in the previous tutorials can be defined directly for the flow. By doing so a controller class doesn’t need to now how an action is implemented. It must only now the specific id of the action and which node should trigger the action. This structure can be very helpful if default views should be used in multiple flows or if controller classes and / or action classes are part of different modules that don’t depend on each other. Let’s think about the following structure:
Bildschirmfoto 2014-06-08 um 22.03.20
In this example the ViewController1.class, ViewController2.class and CustomAction.class don’t know each other. With the help of the DataFX flow API you can simply combine them by using:

new Flow(View1Controller.class).
withLink(View1Controller.class, "link-id", View2Controller.class).
withAction(View2Controller.class "callAction", new CustomAction()).
startInStage(primaryStage);

As a last step I want to extent the example application and add a help output. This should be a global action that prints some help on the console. To do so the action is registered for the flow:

new Flow(WizardStartController.class).
withLink(WizardStartController.class, "next", Wizard1Controller.class).
...
withGlobalTaskAction("help", () -> System.out.println("## There is no help for the application :( ##")).
startInStage(primaryStage);

As you can see in the code you can simply pass a lambda expression as a action to the flow because the FlowTaskAction class that is used internally here defines the action as a Runnable that is a function interface since Java8.
Once this is done the action can be triggered in any view:

@FXMLController(value="wizard1.fxml", title = "Wizard: Step 1")
public class Wizard1Controller extends AbstractWizardController {

    @FXML
    @ActionTrigger("help")
    private Hyperlink helpLink;
}

When looking at the source code of the tutorial you will see that the “help” action isn’t triggered in all views. That is no problem for DataFX. A global action mustn’t be called in each view and even a normal action mustn’t be called in the defined controller. The API only defines that there is an action with the given id that could be called. For this last step a hyperlink tag is added to the FXML files. Here is a screenshot of the final wizard:
dialog-desc