Your First WaterUI App
Now that your development environment is set up, let's build your first interactive WaterUI application! We'll create a counter app that demonstrates the core concepts of views, state management, and user interaction.
What We'll Build
Our counter app will feature:
- A display showing the current count
- Buttons to increment and decrement the counter
- A reset button
- Dynamic styling based on the counter value
By the end of this chapter, you'll understand:
- How to create interactive views
- How to manage reactive state
- How to handle user events
- How to compose views together
Setting Up the Project
Create a new project for our counter app:
cargo new counter-app
cd counter-app
Update your Cargo.toml
:
Filename: Cargo.toml
[package]
name = "counter-app"
version = "0.1.0"
edition = "2021"
[dependencies]
waterui = { path = ".." }
waterui_gtk4 = { path = "../backends/gtk4" }
Building the Counter Step by Step
Let's build our counter app incrementally, learning WaterUI concepts along the way.
Step 1: Basic Structure
Start with a simple view structure. Since our initial view doesn't need state, we can use a function:
Filename: src/main.rs
use waterui::View;
use waterui_gtk4::{Gtk4App, init};
pub fn counter() -> impl View {
"Counter App"
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
init()?;
let app = Gtk4App::new("com.example.counter-app");
Ok(app.run(counter).into())
}
Run this to make sure everything works:
cargo run
You should see a window with "Counter App" displayed.
Step 2: Adding Layout
Now let's add some layout structure using stacks:
use waterui::{component::layout::stack::vstack, View};
pub fn counter() -> impl View {
vstack((
"Counter App",
"Count: 0",
))
}
Note:
vstack
creates a vertical stack of views. We'll learn abouthstack
(horizontal) andzstack
(overlay) later.
Step 3: Adding Reactive State
Now comes the exciting part - let's add reactive state! We'll use the s!
macro from nami for reactive computations and the text!
macro for reactive text:
use waterui::{
component::{
layout::stack::{vstack, hstack},
button::button,
},
View,
};
use waterui_text::text;
use waterui::reactive::binding;
pub fn counter() -> impl View {
let count = binding(0);
vstack((
"Counter App",
// Use text! macro for reactive text
text!("Count: {}", count),
hstack((
button("- Decrement").action_with(&count, |count| count.update(|n| n - 1)),
button("+ Increment").action_with(&count, |count| count.update(|n| n + 1)),
)),
))
}
Run this and try clicking the buttons! The counter should update in real-time.
Understanding the Code
Let's break down the key concepts introduced:
Reactive State with binding
let count = Binding::int(0);
This creates a reactive binding with an initial value of 0. When this value changes, any UI elements that depend on it will automatically update.
Reactive Text Display
// ✅ Use the text! macro for reactive display
text!("Count: {}", count)
- The
text!
macro automatically handles reactivity - The text will update whenever
count
changes
Event Handling
button("- Decrement").action_with(&count, |count| count.update(|n| n - 1))
.action_with()
attaches an event handler with captured stateBinding<T>::update(|v| ...)
updates the value and notifies watchers
Layout with Stacks
vstack((...)) // Vertical stack
hstack((...)) // Horizontal stack
Stacks are the primary layout tools in WaterUI, allowing you to arrange views vertically or horizontally.