tactile 0.1.0
tactile: ^0.1.0 copied to clipboard
Make any widget feel physical: it tilts toward your finger, depresses at the press point, and a glare tracks where you press. Compositor-only, no shaders.
tactile #
Make any Flutter widget feel physical when you touch it. It tilts toward your finger, depresses at the exact press point, and a specular highlight tracks where you press.
No shaders. Compositor-only. Works on iOS, Android, web, macOS, Windows, and Linux at full frame rate.
Tactile(
onTap: () => print('tapped'),
child: const FlutterLogo(size: 120),
)
[A card tilting toward the finger with a glare that tracks the press point]
Why #
Flutter has neumorphism packages for the static soft-extruded look. None of
them make interaction press-position-aware — the visual response that
follows where and how you touch. That's what tactile does, and it works on
any child, not just buttons.
Install #
dependencies:
tactile: ^0.1.0
Usage #
Wrap anything:
Tactile(
borderRadius: BorderRadius.circular(16),
onTap: () {},
child: myWidget,
)
Presets #
Tactile.subtle(child: ...) // restrained — cards, list rows, dense UI
Tactile.playful(child: ...) // exaggerated — hero buttons, demo scenes
Styled components #
Batteries-included widgets built on Tactile that own their surface, so
they also morph their neumorphic shadows from extruded to flush as you press:
TactileButton(onTap: () {}, child: const Text('Press me'))
TactileCard(child: ...) // leans like a held card
TactileTile( // list row
leading: const Icon(Icons.bolt),
title: const Text('Title'),
subtitle: const Text('Subtitle'),
trailing: const Icon(Icons.chevron_right),
onTap: () {},
)
Pass a TactileStyle to control the surface (color, gradient,
borderRadius, elevation, lightDirection) and the feel (tilt, depress,
glareIntensity). Neumorphic shadows read best when color is close to the
surrounding background.
Tuning #
| Parameter | Default | What it does |
|---|---|---|
tilt |
0.15 |
Max perspective rotation toward the press point (radians). 0 disables. |
depress |
0.04 |
Fractional scale-in at the touch point. 0.04 → shrinks to 96%. |
glare |
true |
Moving specular highlight overlay. |
glareColor |
Colors.white |
Highlight color. |
glareIntensity |
0.35 |
Peak glare opacity at full press. |
borderRadius |
BorderRadius.zero |
Clips the glare to the child's rounded shape. |
springBack |
true |
Release uses spring physics (vs. reversed curve). |
pressCurve |
Curves.easeOut |
Curve while pressing in. |
pressDuration |
90ms |
Press-in duration. |
enabled |
true |
When false, no effects and callbacks don't fire. |
How it works #
The press point drives everything — touch position is normalized to [-1, 1]
from the child's center, and a single press-progress value scales both the tilt
magnitude and depress depth, so release springs them back together:
- Tilt — a perspective
Matrix4rotation about X/Y toward the finger. - Depress —
Transform.scalewith its origin offset to the touch point, so the child shrinks toward your finger rather than its center. - Glare — a radial gradient painted on top, centered at the finger and
clipped to
borderRadius. - Spring-back — a
SpringSimulationreturns press-progress to rest on release, with a little overshoot.
Effects are transform + overlay only and wrapped in a RepaintBoundary, so they
run on the compositor and never touch the child's layout.
Accessibility #
- Respects the platform reduce-motion setting: tilt and glare are dropped, leaving only a quiet depress as the press affordance.
- Exposes button semantics and a tap action when
onTapis provided. - Supports keyboard activation (Enter/Space) when focused.
enabled: falsedisables all effects and callbacks.- Tracks a single pointer; multi-touch can't tear the effect in two directions.
- The effect follows your finger as you drag, while
onTapis suppressed once the press becomes a drag (so dragging never counts as a tap).
Scrollables:
Tactileobserves pointers passively (it doesn't claim the gesture arena), so aListView/PageViewstill scrolls normally — the effect simply animates alongside the drag. A dedicated gesture recognizer for tighter coexistence is planned.
Example #
A gallery with recordable scenes lives in example/. Run it with:
cd example && flutter run
Status #
v0.1.0 ships the core Tactile wrapper plus the styled components
(TactileButton, TactileCard, TactileTile) with neumorphic shadow-morph.
A dedicated gesture recognizer for tighter scrollable coexistence is next.
Development #
This repo is pinned to a Flutter version with FVM
(see .fvmrc). With FVM installed:
fvm install # gets the pinned Flutter version
fvm flutter pub get
fvm flutter test
fvm flutter analyze
(Plain flutter works too if your global SDK satisfies the constraints in
pubspec.yaml.)
License #
MIT © 2026