Skip to main content

InkWell

Purpose​

Makes any content pressable, with an ink ripple, hover and focus feedback.

Signature​

pub struct InkWell<Msg = crate::callback::Callback> { .. }

impl InkWell {
pub fn new() -> Self;
}

Example​

Rendered preview of the InkWell demo

From frus-examples/widgets/src/features/interaction/widgets/ink_well.rs:

//! `InkWell` — makes any content pressable, with the ink ripple that spreads from the
//! point of contact, a hover tint and a focus highlight.
//!
//! Use it for custom tiles and rows that should feel like buttons. It answers a click and
//! a long press; for more gestures, put a `GestureDetector` around it.

use frus::{
component, Align, Color, Column, Container, Icon, Icons, InkWell, Row, Text, Widget, Wrap,
};

use crate::common::shared::{section, sections};

pub fn example() -> Box<dyn Widget> {
Box::new(component(|cx| {
let presses = cx.use_state(|| 0_u32);
let holds = cx.use_state(|| 0_u32);
let scheme = &cx.theme().scheme;

// A tile: the ink is clipped to `radius`, which should match the content's corners.
let (press, hold) = (presses.clone(), holds.clone());
let tile = InkWell::new()
.radius(16.0)
.on_click(move || press.update(|n| *n += 1))
.on_long_press(move || hold.update(|n| *n += 1))
.child(
Container::new()
.width(160.0)
.padding(16.0)
.radius(16.0)
.border(1.0, scheme.outline_variant)
.child(
Column::new()
.spacing(8.0)
.cross_axis_alignment(Align::Start)
.child(Icon::new(Icons::TOUCH_APP).color(scheme.primary))
.child(Text::new("Press or hold me")),
),
);

// The splash colour is yours to choose; the theme's is used otherwise.
let press_row = presses.clone();
let row = InkWell::new()
.radius(8.0)
.color(Color::rgba(0.9, 0.2, 0.4, 0.25))
.on_click(move || press_row.update(|n| *n += 1))
.child(
Container::new().padding(12.0).child(
Row::new()
.spacing(12.0)
.cross_axis_alignment(Align::Center)
.child(Icon::new(Icons::FAVORITE).color(Color::hex("#E11D48")))
.child(Text::new("A row with a custom splash")),
),
);

sections(vec![
Box::new(section(
"Counts",
Text::new(format!(
"{} presses, {} long presses",
presses.get(),
holds.get()
))
.color(scheme.primary),
)),
Box::new(section(
"Tiles and rows",
Wrap::new().gap(16.0).run_gap(16.0).child(tile).child(row),
)),
])
}))
}

Properties​

MethodArgumentEffect
.color()color: ColorOverrides the splash colour, alpha included — the caller decides how present the ink is, not just what hue it takes.
.radius()radius: impl Into<BorderRadius>The rounding the ink is clipped to (uniform via f32, per corner via BorderRadius). It should match whatever draws the surface underneath.
.on_click()msg: impl Into<Msg>The message a tap emits.
.on_long_press()msg: impl Into<Msg>The message a long press emits.
.child()child: impl Widget<Msg> + 'staticSets the child.

Behaviour​

A pass-through in layout, like crate::ClipRRect: it takes the size its parent gives it and its child fills it. What it adds is the ink, and the tap that starts it.

InkWell::new()
.radius(12.0) // the ink is clipped to this rounding
.on_click(Msg::Open(id))
.child(row)

InkWell::new().color(theme.scheme.primary.fade(0.16)).child(tile) // its own colour

The splash sits over this widget and under its child, so a row of text tapped this way keeps its text on top of the ink, the way a material surface does.

See also​

Source: crates/frus-widgets/src/ink.rs @ e6fba33f5b42