Java
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Java
-
Section 4: Collections Framework
-
Section 5: Exception Handling
-
Section 6: Generics
-
Section 7: Functional Java
-
Section 8: Concurrency
-
Section 9: I/O and NIO
-
Section 10: JVM Internals
-
Section 11: Modern Java Features
-
Section 12: Build Tools and Project Structure
-
Section 13: Testing
-
Section 14: Databases and Persistence
-
Section 15: Networking
-
Section 16: Design and Best Practices
-
Section 17: Reflection and Annotations
-
Section 18: Logging and Diagnostics
-
Section 19: Date, Time, and Internationalization
-
Section 20: Java Platform Module System
-
Section 21: Security in Java
-
Section 22: Advanced Collections and Data Structures
-
Section 23: More Concurrency Patterns
-
Section 24: Compression, Files, and System Integration
-
Section 25: GUI Programming
-
Section 26: Practical Projects
-
Section 27: Data Structures and Algorithms
-
Section 28: Interview and Algorithm Practice
-
Section 29: JSON and Data Interchange
-
Section 30: More Concurrency Utilities
-
Section 31: More Collections and Streams Practice
-
Section 32: More File and System Programming
-
Section 33: Standard Library Deep Dive
-
Section 34: More Practice and Drills
-
Section 35: More Testing and Quality
-
Section 36: More Design Patterns and Architecture
-
Section 37: Career and Ecosystem
-
Section 38: More OOP and Architecture Practice
-
Section 39: More Enterprise Concepts
-
Section 40: Advanced JavaFX
-
Section 41: More Interview Practice
214: Building a Simple Todo App with JavaFX
By now, you've got the basics of Java down, and you're likely itching to build something you can actually see and interact with. JavaFX is great for this, but there is a trap that almost every developer falls into the first time they build a GUI: the "Everything-in-the-Start-Method" pattern. It feels efficient at first, but it's a recipe for a maintenance nightmare.
The Temptation of the Single-Class App
When you start building a Todo app, your instinct will be to instantiate your TextField, your Button, and your VBox right inside the start() method. You'll probably write a lambda for the button click that grabs the text from the field and manually adds a new Label to the layout. It looks like this:
public void start(Stage primaryStage) {
TextField input = new TextField();
VBox todoList = new VBox();
Button addBtn = new Button("Add");
addBtn.setOnAction(e -> {
String task = input.getText();
todoList.getChildren().add(new Label(task));
input.clear();
});
primaryStage.setScene(new Scene(new VBox(input, addBtn, todoList)));
primaryStage.show();
}
I'll be honest: for a three-field demo, this is fine. But the moment you want to add a "Delete" button to each task, or a way to save your list to a file, this approach collapses. You're mixing your data (the task string), your business logic (adding a task), and your view (the Label) into one giant block of code. If you want to change how a task looks—say, adding a checkbox—you have to dig into the event handler of a button that is buried inside your main application setup.
When the UI Logic Chokes the Application
The real problem here is that your "Todo item" doesn't actually exist as an object; it only exists as a Label in a VBox. This is a classic mistake. You've coupled your data directly to the UI component. If you ever decided to switch from a VBox to a ListView, you'd have to rewrite every single line of your logic because your "data" is literally a piece of the user interface.
As a colleague, I'd tell you: stop thinking about Labels and start thinking about Tasks. You need a way to track the state of a todo item independently of how it is drawn on the screen. If your app crashes or you want to implement an "Undo" feature, you can't do that if your only record of the task is a UI element sitting in a container.
Decoupling State from Layout
The professional way to handle this is to separate your data model from your view. First, create a simple TodoItem class. Then, instead of manually adding nodes to a pane, use an ObservableList. This is where JavaFX gets powerful. An ObservableList can be bound to a UI component, meaning when you update the list in Java, the UI updates itself automatically.
Here is how I would structure this. I'd create a TodoItem POJO to hold the description and completion status, and then use a ListView coupled with a CellFactory. This separates what a task is from how it's rendered.
public class TodoItem {
private final StringProperty description = new SimpleStringProperty();
public TodoItem(String text) { description.set(text); }
public StringProperty descriptionProperty() { return description; }
public String getDescription() { return description.get(); }
}
// In your main app:
ObservableList<TodoItem> masterData = FXCollections.observableArrayList();
ListView<TodoItem> listView = new ListView<>(masterData);
// Tell the ListView how to display the TodoItem object
listView.setCellFactory(lv -> new ListCell<>() {
@Override
protected void updateItem(TodoItem item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item.getDescription());
}
});
addBtn.setOnAction(e -> {
masterData.add(new TodoItem(input.getText()));
input.clear();
});
Now, the addBtn doesn't know that a ListView exists. It only knows that it's adding a TodoItem to a list. If you decide tomorrow that you want to display your tasks in a table or a grid instead of a list, you only change the UI component—your logic for adding and managing tasks remains untouched. That's the difference between a script that works and a piece of software that lasts.
📋 Practical Task
Exercise: Implement Task Deletion in the Decoupled Todo App
Using the decoupled architecture from the lesson (where tasks are stored in an ObservableList<TodoItem> and displayed via a ListView), implement a "Remove Selected" feature.
Your goal is to add a "Delete" button to the layout. When clicked, it should identify which TodoItem is currently selected in the ListView and remove that object from the ObservableList. Ensure that the UI updates automatically upon removal and that the application does not crash if the user clicks "Delete" without having an item selected.
There are no comments for now.