| Crates.io | waterui-controls |
| lib.rs | waterui-controls |
| version | 0.2.1 |
| created_at | 2025-12-14 08:04:34.806625+00 |
| updated_at | 2025-12-14 11:29:48.728413+00 |
| description | Form components for WaterUI (inputs, buttons, validation) |
| homepage | |
| repository | https://github.com/water-rs/waterui |
| max_upload_size | |
| id | 1983991 |
| size | 62,892 |
Interactive UI controls for WaterUI applications with reactive data binding.
waterui-controls provides a complete set of form controls and interactive components for WaterUI applications. Each control integrates seamlessly with WaterUI's reactive system through Binding<T> and Computed<T>, enabling automatic UI updates when data changes. Controls render to native platform widgets (UIKit/UIKit/AppKit on Apple, Android Views/Android View on Android), providing a truly native look and feel.
This crate is part of the WaterUI workspace and is re-exported through the main waterui crate's prelude, so most users will access these components via use waterui::prelude::* rather than depending on this crate directly.
Add to your Cargo.toml:
[dependencies]
waterui-controls = "0.1.0"
Or use the main waterui crate which re-exports all controls:
[dependencies]
waterui = "0.2"
use waterui::prelude::*;
use waterui::reactive::binding;
let name = binding("");
let age = binding(25);
let enabled = binding(true);
let volume = binding(0.5);
vstack((
// Text input with label
field("Name", &name),
// Numeric stepper
stepper(&age).label("Age").range(0..=120),
// Boolean toggle
toggle("Enabled", &enabled),
// Range slider
slider(0.0..=1.0, &volume).label("Volume"),
// Submit button
button("Submit").action(|| {
tracing::debug!("Form submitted!");
}),
))
All controls accept reactive bindings that enable two-way data synchronization:
Binding<T>: Mutable reactive state that updates both when the UI changes and when modified programmaticallyComputed<T>: Read-only derived values that automatically update when dependencies changelet count = binding(0);
// UI updates when count changes programmatically
count.set(5);
// count updates when user interacts with stepper
stepper(&count)
Controls have different layout characteristics:
TextField, Slider, Toggle, Stepper): Expand to fill available widthButton): Size to fit their contentProgress): Linear style stretches, circular style is content-sizedTriggers actions when clicked. Supports multiple visual styles.
use waterui::prelude::*;
// Basic button
button("Click me").action(|| {
tracing::debug!("Button clicked!");
});
// Styled button
button("Submit")
.style(ButtonStyle::BorderedProminent)
.action(|| {
tracing::debug!("Form submitted!");
});
// Link-style button
button("Learn more")
.style(ButtonStyle::Link)
.action(|| {
tracing::debug!("Opening link...");
});
Available styles: Automatic (default), Plain, Link, Borderless, Bordered, BorderedProminent
A switch control for boolean values.
use waterui::prelude::*;
use waterui::reactive::binding;
let wifi_enabled = binding(true);
// Toggle with label
toggle("Wi-Fi", &wifi_enabled)
// Toggle without label (just the switch)
Toggle::new(&wifi_enabled)
Continuous value selection within a range.
use waterui::prelude::*;
use waterui::reactive::binding;
let brightness = binding(0.5);
// Basic slider (0.0 to 1.0)
slider(0.0..=1.0, &brightness)
.label("Brightness")
.min_value_label("Dark")
.max_value_label("Bright")
// Volume control
let volume = binding(50.0);
slider(0.0..=100.0, &volume).label(text!("{:.0}%", volume))
Increment or decrement integer values.
use waterui::prelude::*;
use waterui::reactive::binding;
let quantity = binding(1);
// Basic stepper (shows current value)
stepper(&quantity)
// Stepper with label and constraints
stepper(&quantity)
.label("Items")
.range(1..=10)
.step(1)
// Custom value formatting
stepper(&quantity)
.value_formatter(|n| format!("{} items", n))
Single-line or multi-line text input.
use waterui::prelude::*;
use waterui::reactive::binding;
let email = binding("");
let bio = binding("");
// Single-line text field
TextField::new(&email)
.prompt("Enter your email")
// With label (convenience function)
field("Email", &email)
// Multi-line text field
TextField::new(&bio)
.line_limit(5)
.prompt("Tell us about yourself")
Keyboard types: Text (default), Email, URL, Number, PhoneNumber
Multi-line text editor with styled text support.
use waterui::prelude::*;
use waterui::reactive::binding;
use waterui::text::styled::StyledStr;
let content = binding(StyledStr::default());
RichTextEditor::new(&content)
.placeholder("Start writing...")
.disable_line_limit()
use waterui::prelude::*;
use waterui::reactive::binding;
let username = binding("");
let age = binding(18);
let terms_accepted = binding(false);
vstack((
field("Username", &username)
.prompt("Enter username"),
stepper(&age)
.label("Age")
.range(13..=120),
toggle("Accept Terms", &terms_accepted),
button("Register")
.style(ButtonStyle::BorderedProminent)
.action(|| {
tracing::debug!("Registration submitted");
}),
))
.padding_with(EdgeInsets::all(16.0))
use waterui::prelude::*;
use waterui::reactive::binding;
let dark_mode = binding(false);
let notifications = binding(true);
let volume = binding(0.7);
let font_size = binding(16);
scroll(
vstack((
text("Settings").size(24.0).bold(),
Divider,
toggle("Dark Mode", &dark_mode),
toggle("Notifications", ¬ifications),
Divider,
slider(0.0..=1.0, &volume)
.label("Volume"),
stepper(&font_size)
.label("Font Size")
.range(12..=24)
.step(2),
))
.padding_with(EdgeInsets::all(16.0))
)
use waterui::prelude::*;
use waterui::reactive::binding;
let count = binding(0);
vstack((
text!("Count: {}", count).size(32.0),
hstack((
button("Decrement").action_with(&count, |count| {
count.update(|n| n - 1);
}),
stepper(&count).range(0..=100),
button("Increment").action_with(&count, |count| {
count.update(|n| n + 1);
}),
)),
button("Reset")
.style(ButtonStyle::Borderless)
.action_with(&count, |count| {
count.set(0);
}),
))
use waterui::prelude::*;
use waterui::reactive::binding;
let progress = binding(0.0);
let is_loading = binding(false);
vstack((
// Progress bar
progress(progress.clone()),
// Indeterminate loading spinner
loading().visible(is_loading.clone()),
// Control buttons
hstack((
button("Start").action_with(&is_loading, |loading| {
loading.set(true);
}),
button("Stop").action_with(&is_loading, |loading| {
loading.set(false);
}),
)),
))
Button: Clickable button with customizable styles and actionsToggle: Boolean switch controlSlider: Continuous range selector (f64)Stepper: Discrete numeric adjuster (i32)TextField: Single/multi-line text inputRichTextEditor: Styled text editorbutton(label): Create a button with a labeltoggle(label, binding): Create a labeled toggleslider(range, binding): Create a sliderstepper(binding): Create a stepperfield(label, binding): Create a labeled text fieldButtonStyle: Visual styles for buttons (Automatic, Plain, Link, Borderless, Bordered, BorderedProminent)KeyboardType: Keyboard types for text fields (Text, Email, URL, Number, PhoneNumber)This crate currently has no optional features. All components are included by default.
All controls render to native platform widgets:
This ensures controls match the platform's design language and accessibility features automatically.