bzzz!

pub package

Professional-grade haptic feedback for Flutter. Full control over vibration patterns on iOS (CoreHaptics) and Android (VibrationEffect), or just use the built-in presets and move on.

Quick Start

dependencies:
  bzzz: ^0.1.0
import 'package:bzzz/bzzz.dart';

// Play a preset
await Bzzz.instance.play(HapticCollection.success);

// Stop playback (useful for long patterns)
await Bzzz.instance.cancel();

play plays the haptic and swallows errors silently — your app won't crash on devices without a vibration motor. If you want to handle errors explicitly, use playOrThrow.

HapticCollection has 22 built-in presets for common cases: clicks, success/error feedback, heartbeat, rumble, sparkle, and more. See full list below.

Platform Support

Platform Min version Engine
iOS 13+ CoreHaptics
Android API 26+ VibrationEffect

The plugin declares android.permission.VIBRATE in its manifest automatically.

Android: How Vibration Works Under the Hood

Android has fragmented haptics support. The library handles this by providing a tiered fallback system. You supply up to three configs in a single AndroidHapticConfig, and the library picks the best one the device can run:

Priority API What it does
1 EnvelopeConfig (API 36+) VibrationEffect.BasicEnvelopeBuilder — full control over intensity + sharpness curves over time. Closest to CoreHaptics on iOS.
2 CompositionConfig (API 30+) VibrationEffect.startComposition() — chains hardware primitives (click, thud, spin, quickRise, slowRise, quickFall, tick, lowTick) with scale and delay between them.
3 WaveformConfig (API 26+) VibrationEffect.createWaveform() — timed on/off pattern with optional amplitude control (0–255). If the device has no amplitude control, it falls back to binary on/off.

All values you pass are normalized ([0.0, 1.0] for intensities, seconds for durations). The native layer converts them to platform units (milliseconds, 0–255 amplitudes, etc.).

If none of the provided configs match the device capabilities, playback silently does nothing.

Using Presets

HapticCollection has 22 ready-to-use patterns:

// Basic UI feedback
HapticCollection.click
HapticCollection.lightClick
HapticCollection.heavyClick
HapticCollection.selection
HapticCollection.success
HapticCollection.error
HapticCollection.snap

// Expressive
HapticCollection.heartbeat
HapticCollection.charge
HapticCollection.sparkle
HapticCollection.drumRoll
HapticCollection.rumble
HapticCollection.boing
HapticCollection.wave
HapticCollection.coinDrop
HapticCollection.waterDrip
// ...and more

Some presets accept parameters:

await Bzzz.instance.play(ClickHaptic(intensity: 0.4));

Custom Haptics

If presets aren't enough, you have two options depending on how much control you need.

UnifiedHaptic — one config for both platforms

Define a haptic with normalized parameters, and the library translates it for each platform:

await Bzzz.instance.play(
  UnifiedHaptic(
    intensity: 0.7,
    sharpness: 0.5,
    duration: 0.3,
    attack: 0.1,
    decay: 0.2,
  ),
);

All values are normalized to [0.0, 1.0], durations are in seconds.

CustomHaptic — full per-platform control

When you need to squeeze out every bit of the hardware, configure each platform separately:

await Bzzz.instance.play(
  CustomHaptic(
    cupertinoConfig: CupertinoHapticConfig(
      events: [
        HapticTransient(time: 0.0, intensity: 1.0, sharpness: 0.8),
        HapticContinuous(
          time: 0.1,
          duration: 0.5,
          intensity: 0.6,
          sharpness: 0.4,
        ),
      ],
      parameterCurves: [
        ParameterCurve(
          parameterId: CurveParameterId.hapticIntensityControl,
          controlPoints: [
            ParameterCurveControlPoint(time: 0.0, value: 1.0),
            ParameterCurveControlPoint(time: 0.5, value: 0.0),
          ],
        ),
      ],
    ),
    androidConfig: AndroidHapticConfig(
      compositionConfig: CompositionConfig(
        primitives: [
          CompositionPrimitive(
            primitive: AndroidPrimitive.click,
            scale: 1.0,
            delay: 0,
          ),
        ],
      ),
    ),
  ),
);

On iOS you describe haptics as a timeline of transient/continuous events with optional parameter curves (ADSR envelopes, intensity/sharpness animations over time).

On Android the library picks the best available API automatically based on what the device supports (see Android section above). You can provide all three tiers in one config and the library will use the best match.

Checking Device Capabilities

final info = await Bzzz.instance.getDeviceHapticsInfo();

switch (info) {
  case AndroidDeviceHapticsInfo():
    print('Vibrator: ${info.hasVibrator}');
    print('Amplitude control: ${info.hasAmplitudeControl}');
    print('Primitives: ${info.supportedPrimitives}');
  case CupertinoDeviceHapticsInfo():
    print('CoreHaptics: ${info.supportsCoreHaptics}');
}

More Examples

The example app has a lot of patterns — from practical UI feedback (toggle switches, pull-to-refresh, message sent) to fun stuff (typewriter, raindrops, explosions) and even recognizable tunes (Imperial March, Beethoven's 5th). Good starting point if you want to design your own.

License

MIT

Libraries

bzzz
Flutter Haptics — rich cross-platform haptic feedback.