HapWeave
Weave meaningful haptics into Flutter.
HapWeave is a capability-aware Flutter plugin for semantic feedback and custom haptic patterns on iOS and Android. It selects the highest-quality native implementation available on each device and reports when a fallback was used.
HapWeave is in early development. Its public API may change before
1.0.0.
Why HapWeave?
Flutter's built-in haptic API intentionally exposes a small set of platform defaults. HapWeave adds:
- 30 presets for common interactions, gestures, state, and expression;
- transient and continuous events on a shared timeline;
- intensity and sharpness controls from
0.0to1.0; - parameter curves for continuous feedback;
- device capability inspection and automatic fallbacks;
- playback diagnostics for development and device testing.
HapWeave aims for the same meaning and perceived intent across devices, not identical physical waveforms. Actuators and manufacturer tuning differ.
Platform support
| Platform | Minimum | Highest-quality backend |
|---|---|---|
| iOS | 13.0 | Core Haptics |
| Android | API 26 | Basic Envelope on API 36+, Composition on API 30+, Waveform otherwise |
Some devices do not contain compatible haptic hardware. Always treat haptics as an enhancement rather than the only way to communicate state.
Installation
Add HapWeave from pub.flutter-io.cn:
flutter pub add hap_weave
Or add it manually to pubspec.yaml:
dependencies:
hap_weave: ^0.1.0
Then import the package with
import 'package:hap_weave/hap_weave.dart';.
Preset catalog
HapWeave includes 30 presets. Preset names describe intent rather than a fixed motor waveform, so the result can be adapted to each platform and device.
import 'package:hap_weave/hap_weave.dart';
await HapWeave.instance.play(HapticPreset.selection);
await HapWeave.instance.play(HapticPreset.longPressComplete);
await HapWeave.instance.play(HapticPreset.success);
await HapWeave.instance.play(HapticPreset.heartbeat);
Basic interactions
| Preset | Intended use | Feel |
|---|---|---|
selection |
Picker or segmented-control changes | Very light tick |
tap |
Ordinary taps | Familiar light impact |
softTap |
Secondary actions | Soft, restrained impact |
firmTap |
Prominent primary actions | Crisp, weighty impact |
toggle |
Neutral switch changes | Soft single response |
toggleOn |
Enabling a feature | Rising two-step response |
toggleOff |
Disabling a feature | Falling two-step response |
longPressStart |
Acknowledging that a hold began | Short, soft continuous touch |
longPressComplete |
Committing a hold action | Soft cue followed by a firm cue |
dragTick |
Slider or scrubber steps | Tiny sharp tick |
snap |
Locking an object into position | Short, sharp stop |
Gestures and movement
| Preset | Intended use | Feel |
|---|---|---|
swipeCommit |
Crossing a swipe action threshold | Light-to-firm pair |
pullReady |
Pull-to-refresh release threshold | Gradual rising pull |
refreshComplete |
Refreshed content has arrived | Light two-step finish |
reorderLift |
Picking up a reorderable item | Decaying lift sensation |
reorderDrop |
Placing a reordered item | Two-step landing |
boundary |
Reaching a range or scroll edge | Firm stop with soft rebound |
State feedback
| Preset | Intended use | Feel |
|---|---|---|
confirm |
Confirming a decision | Decisive single impact |
success |
Save, send, or operation success | Positive system-style response |
warning |
A state requiring attention | Warning system-style response |
error |
A failed action | Unmistakable error response |
notification |
General incoming information | Compact notification response |
completion |
Completing a longer task | Rewarding completion response |
delete |
Removing content | Heavy cue that falls away |
denied |
Locked or unavailable actions | Two firm stops |
copied |
Copying content | Two tiny crisp ticks |
Expressive patterns
| Preset | Intended use | Feel |
|---|---|---|
doublePulse |
Emphasis or lightweight reactions | Two even beats |
triplePulse |
Special attention | Three rising beats |
heartbeat |
Favorites or warm emotional moments | Two repeated lub-dub pairs |
crescendo |
Achievement or building anticipation | Continuous rise to a strong finish |
The original platform-semantic presets use UIKit feedback generators and Android predefined effects where available. The additional authored presets use HapWeave's event timeline so their timing and intensity remain intentional. Hardware differences mean they will not feel physically identical on every device; always validate important interactions on real iOS and Android devices.
Custom patterns
Build a pattern as a sequence:
final pattern = HapticSequence()
.transient(intensity: 0.3, sharpness: 0.9)
.wait(const Duration(milliseconds: 70))
.transient(intensity: 0.8, sharpness: 0.5)
.build();
final playback = await HapWeave.instance.play(pattern);
print(playback.backend);
print(playback.wasFallback);
Continuous effects can change over time:
final crescendo = HapticSequence()
.continuous(
duration: const Duration(milliseconds: 600),
intensity: 0.7,
sharpness: 0.3,
intensityCurve: HapticCurve(const [
HapticPoint(0.0, 0.1),
HapticPoint(0.6, 0.5),
HapticPoint(1.0, 1.0),
]),
)
.build();
await HapWeave.instance.play(crescendo);
Patterns are limited to ten seconds. This keeps the API focused on interface feedback and helps prevent accidental long, uncomfortable vibrations.
Playback policies
await HapWeave.instance.play(
HapticPreset.confirm,
policy: HapticPlaybackPolicy.ignoreIfPlaying,
fallback: HapticFallbackPolicy.automatic,
);
await HapWeave.instance.stop();
replace: stop the current effect and play immediately;ignoreIfPlaying: skip the request while another effect is active;enqueue: wait until the current effect's estimated duration has elapsed.
Fallback policies are automatic for the best available translation, system
to request a simple platform effect, and none to prevent custom-pattern
fallbacks.
Capabilities and diagnostics
final capabilities = await HapWeave.instance.capabilities;
print(capabilities.supportLevel);
print(capabilities.supportsSharpnessControl);
final playback = await HapWeave.instance.play(HapticPreset.success);
print(playback.backend);
print(playback.quality);
print(playback.fallbackReason);
Try it on a device
Haptics cannot be evaluated accurately in a simulator. Connect an iPhone or Android device and run the included laboratory app:
cd example
flutter run
The app is available in English and Japanese. Each preset is demonstrated with a matching UI control—such as a switch, long press, slider, swipe, reorder, copy, or delete action—rather than an undifferentiated grid of play buttons. It also includes a visual custom-pattern designer with a draggable event timeline, an intensity/sharpness feel pad, undo and redo, device playback, and Dart code export.
Learn how haptics work
Project scope
HapWeave targets general mobile applications. Frame-accurate game feedback, audio-haptic synchronization, direct frequency authoring, AHAP file playback, desktop platforms, and web are not part of the initial release.
Development
flutter analyze
flutter test
cd example && flutter test
See CONTRIBUTING.md before proposing public API or preset changes. Presets should be tuned and reviewed on physical iOS and Android hardware.
License
MIT. See LICENSE.