advanced_haptics 1.2.0 copy "advanced_haptics: ^1.2.0" to clipboard
advanced_haptics: ^1.2.0 copied to clipboard

A Flutter plugin for playing advanced, custom haptic feedback patterns on Android and iOS, including waveforms and Core Haptics .ahap files.

Advanced Haptics #

A Flutter plugin for playing powerful, custom haptic feedback patterns. This package provides a unified API for Android and iOS, giving developers access to fine-grained vibration control and Apple Core Haptics .ahap files.

pub version license


๐Ÿ“š Table of Contents #


โœจ Features #

  • โœ… Unified API: A single, easy-to-use Dart API for both platforms.
  • ๐ŸŽจ Cross-Platform Patterns: Design one HapticPattern of taps and buzzes with intensity and sharpness; it renders natively on both platforms.
  • ๐ŸŽฏ Custom Waveforms: Full control of vibration timing, intensity, and looping.
  • ๐ŸŽ Core Haptics on iOS: Play custom .ahap files and control the player state.
  • ๐Ÿง  Predefined Patterns: A suite of built-in methods like lightTap(), success(), error() and more.
  • ๐Ÿงฉ Native Android Effects: Access system-level vibration effects like tick, heavyClick, etc.
  • ๐Ÿ›ก๏ธ Capability Detection: Easily check if a device supports advanced haptics.
  • ๐Ÿชถ Graceful Fallbacks: Sensible defaults for unsupported hardware or platforms.

๐Ÿ–ฅ Platform Support #

Feature Android (5.0+ / API 21+) iOS (13.0+)
Waveform โœ… API 26+ / ๐Ÿ” Fallback โœ… Emulated (iPhone 8+) / ๐Ÿ” Fallback
.ahap Playback ๐Ÿ” Fallback โœ… Native (iPhone 8+) / ๐Ÿ” Fallback
Player Controls โœ… Emulated โœ… Native
Amplitude Control โœ… API 26+ โœ… Native (iPhone 8+)
Predefined Patterns โœ… API 29+ / ๐Ÿ” Fallback โž– Ignored
Patterns (playPattern) โœ… Primitives API 30+ / ๐Ÿ” Waveform โœ… Core Haptics / ๐Ÿ” Fallback
Composition Primitives โœ… API 30+ / ๐Ÿ” Fallback ๐Ÿ” Core Haptics approximation

โ„น๏ธ Android note: Amplitude control requires API 26 (Android 8.0 Oreo). On older devices the on/off shape of the pattern is played through the legacy vibrator API. Predefined effects (e.g. tick, click) require API 29; older devices play a short approximation. Use hasCustomHapticsSupport() to check for full amplitude support at runtime.

โ„น๏ธ iOS note: iPads and iPhones older than the iPhone 8 do not support Core Haptics. On those devices the plugin falls back to UIFeedbackGenerator taps, and the player controls do nothing. Use hasCustomHapticsSupport() when you need to know whether the full pattern will be played.

๐Ÿ›ก๏ธ Safety: Every method can be called on any platform. Web and desktop (no native implementation), devices without a vibrator, and unsupported hardware silently do nothing. Invalid arguments throw an ArgumentError; native failures surface as a PlatformException (see Error codes).


๐Ÿš€ Getting Started #

1. Install #

Add advanced_haptics to your pubspec.yaml dependencies:

dependencies:
  advanced_haptics: ^1.2.0 # Use the latest version

Then, run flutter pub get in your terminal.

2. Android Setup #

Add the VIBRATE permission to your android/app/src/main/AndroidManifest.xml:

<manifest ...>
    <!-- Add this line -->
    <uses-permission android:name="android.permission.VIBRATE"/>
    <application ...>
    </application>
</manifest>

Kotlin and AGP 9. The plugin follows Flutter's built-in Kotlin guidance: with AGP 9's built-in Kotlin (android.builtInKotlin=true) it never applies the Kotlin Gradle plugin, and with the legacy setting Flutter's templates still write (android.builtInKotlin=false) it uses the Kotlin Gradle plugin that Flutter 3.44+ applies, or applies it itself on older Flutter versions. Existing projects need no changes.

To turn built-in Kotlin on in your app you need Flutter 3.47 or newer (Flutter 3.44's tooling applies the Kotlin Gradle plugin to every plugin regardless of the setting, which AGP 9 rejects). Then follow the app migration guide: stop applying kotlin-android in app/build.gradle(.kts), keep the org.jetbrains.kotlin.android ... apply false declaration in settings.gradle(.kts) at 2.2.20 or newer (Flutter's version check reads it; it is never applied), and set android.builtInKotlin=true in gradle.properties. Verified with Flutter 3.47.4, AGP 9.1.0, Gradle 9.3.1 and Kotlin 2.4.0 declared.


3. iOS Setup #

The plugin requires iOS 13.0 or newer (platform :ios, '13.0' in your Podfile / the Runner deployment target). It ships both a Swift package (used automatically when Swift Package Manager is enabled, the default since Flutter 3.44) and a podspec for CocoaPods; no configuration is needed for either.

To play custom patterns on iOS, add your .ahap files to your project assets (e.g., under an assets/haptics/ folder) and declare the folder in your pubspec.yaml:

flutter:
  assets:
    - assets/haptics/

๐Ÿ“ฆ Usage #

Import the package in your Dart file:

import 'package:advanced_haptics/advanced_haptics.dart';

All of the methods below work on both Android and iOS, with graceful fallbacks where necessary.

โœ… Capability Check #

final bool hasSupport = await AdvancedHaptics.hasCustomHapticsSupport();
if (hasSupport) {
  // Safe to use advanced haptics
}

๐ŸŽจ Design a Pattern Once #

HapticPattern is the recommended way to build custom feedback. Each event has an intensity (strength) and a sharpness (feel, from soft to crisp). On iOS both map directly to Core Haptics. On Android 11+ a tap-only pattern plays as VibrationEffect.Composition primitives (TICK, CLICK or THUD chosen by sharpness), and any other pattern, or any older device, gets the same timeline flattened into an amplitude waveform.

final heartbeat = HapticPatternBuilder()
    .tap(intensity: 0.6, sharpness: 0.3)
    .pause(const Duration(milliseconds: 120))
    .tap(intensity: 1.0, sharpness: 0.3)
    .pause(const Duration(milliseconds: 600))
    .build();

await AdvancedHaptics.playPattern(heartbeat);

// Buzzes are continuous events; events can also be placed at absolute times.
final charge = HapticPatternBuilder()
    .buzz(const Duration(milliseconds: 300), intensity: 0.4, sharpness: 0.2)
    .buzz(const Duration(milliseconds: 300), intensity: 1.0, sharpness: 0.8)
    .add(const HapticTransient(at: Duration(milliseconds: 650), intensity: 1.0))
    .build();
await AdvancedHaptics.playPattern(charge);

// Need the raw Android-style waveform? It is one call away.
final HapticWaveform wave = charge.toWaveform();

Patterns play through the same player as waveforms, so pause(), resume(), seek() and stop() work on them (except when Android rendered the pattern as composition primitives, which cannot be paused).

โšก Predefined Patterns #

Use these for quick, consistent feedback across your app.

await AdvancedHaptics.lightTap();
await AdvancedHaptics.mediumTap();
await AdvancedHaptics.heavyRumble();
await AdvancedHaptics.success();
await AdvancedHaptics.error();

๐ŸŽ› Haptic Player Controls #

Pause, resume and seek the pattern started by playWaveform, playAhap or success. On iOS these drive the CHHapticAdvancedPatternPlayer. Android has no native pause, so the plugin remembers where the waveform is, cancels the vibrator on pause, and replays the remainder (including the repeat loop) on resume or seek. Predefined Android effects cannot be paused. On iOS devices without Core Haptics the controls do nothing.

pause, resume and seek throw a PlatformException with code PLAYER_NIL when no pattern is playing or paused; stop and cancel never do. All atTime values are delays in seconds from now.

await AdvancedHaptics.playWaveform([0, 400, 150, 40], [0, 255, 0, 160], repeat: 2);

// Pause the currently playing haptic pattern
await AdvancedHaptics.pause();

// Resume where it left off
await AdvancedHaptics.resume();

// Jump to 0.5 seconds into the pattern
await AdvancedHaptics.seek(offset: 0.5);

// Cancel all scheduled events and stop immediately
await AdvancedHaptics.cancel();

๐Ÿ›‘ Stop All Vibrations #

Cancels any ongoing haptic effect on either platform. Safe to call when nothing is playing.

// atTime (iOS only): delay in seconds before stopping. 0.0 stops immediately.
await AdvancedHaptics.stop(atTime: 0.0);

๐Ÿค– Android Specific #

These methods expose native Android features. They are safe to call on iOS (waveforms are emulated, predefined effects are ignored) but are designed with Android in mind.

Custom Waveform (Android Preferred)

Design unique patterns with precise control over timings (in milliseconds), amplitudes (0-255), and an optional repeat index. While this is emulated on iOS, it provides the most granular control on Android.

// Plays a pattern once
await AdvancedHaptics.playWaveform(
  [0, 100, 100, 200],     // Timings: [delay, on, off, on]
  [0, 180, 0, 255],       // Amplitudes for each segment
  repeat: -1,             // -1 (default) means no repeat
);

// Loops from index 2 until stop() is called
await AdvancedHaptics.playWaveform(
  [0, 500, 100, 50],
  [0, 255, 0, 120],
  repeat: 2,
);
await Future.delayed(const Duration(seconds: 3));
await AdvancedHaptics.stop();

Both lists must be non-empty and of equal length, timings must be non-negative, amplitudes must be within 0-255, and repeat must be -1 or a valid index; otherwise an ArgumentError is thrown before anything reaches the platform.

On iOS the whole pattern loops when repeat is not -1 (Core Haptics has no loop start point). Segments shorter than 40 ms are rendered as crisp transient taps.

Composition Primitives (API 30+)

Android 11 introduced tuned haptic primitives that feel far crisper than waveform pulses on supporting hardware. playComposition mirrors VibrationEffect.Composition: each primitive has a scale (0-1) and a delay measured from the end of the previous one.

await AdvancedHaptics.playComposition(const [
  AndroidPrimitiveEvent(AndroidHapticPrimitive.click),
  AndroidPrimitiveEvent(AndroidHapticPrimitive.tick, scale: 0.6, delay: Duration(milliseconds: 80)),
  AndroidPrimitiveEvent(AndroidHapticPrimitive.tick, scale: 0.6, delay: Duration(milliseconds: 80)),
  AndroidPrimitiveEvent(AndroidHapticPrimitive.thud, delay: Duration(milliseconds: 200)),
]);

// Optional: check native support (API 30+ and hardware). Defaults to click + tick.
final bool native = await AdvancedHaptics.supportsAndroidPrimitives([AndroidHapticPrimitive.thud]);

Primitives: click, tick, quickRise, slowRise, quickFall (API 30) and thud, spin, lowTick (API 31). Devices that cannot play a primitive, and Android below API 30, get a waveform approximation. On iOS the composition is rendered as the closest Core Haptics events, so the call is safe everywhere.

Native Android Effects (API 29+)

Play Android's built-in system haptic effects using an enum. This has no effect on iOS.

await AdvancedHaptics.playPredefined(AndroidPredefinedHaptic.tick);

Available enums: click, doubleClick, tick, heavyClick (public, always available on API 29+) and thud, pop, ringtone1, textureTick (non-public effect IDs whose support varies by device; unsupported ones fall back to click on API 30+).



๐ŸŽ iOS Specific #

Play .ahap File

Trigger your custom-designed haptic experiences on supported iPhones. This is the highest-fidelity way to play haptics on iOS. The path is a Flutter asset path declared in pubspec.yaml, or an absolute file-system path (e.g. a downloaded file).

await AdvancedHaptics.playAhap('assets/haptics/success.ahap');

// Start playback 0.5 seconds from now
await AdvancedHaptics.playAhap('assets/haptics/success.ahap', atTime: 0.5);

โš ๏ธ Error Codes #

Native failures are reported as a PlatformException with one of these codes:

Code Platform Meaning
INVALID_ARGS both Arguments rejected by the native side.
PERMISSION_DENIED Android android.permission.VIBRATE is missing from the manifest.
VIBRATION_ERROR Android The vibrator service failed.
ENGINE_NIL iOS The Core Haptics engine could not be created.
ENGINE_START_FAILED iOS The engine could not be (re)started, e.g. while the app is in the background.
PATTERN_ERROR iOS The waveform or .ahap file could not be turned into a pattern.
FILE_NOT_FOUND iOS The .ahap asset does not exist.
PLAYBACK_ERROR iOS The player could not be started.
PLAYER_NIL both pause/resume/seek called with no pattern playing or paused.
PLAYER_CONTROL_ERROR iOS pause/resume/seek failed.

๐Ÿงช Testing #

AdvancedHaptics delegates to AdvancedHapticsPlatform.instance, so tests can swap in a fake to record or silence haptic calls:

import 'package:advanced_haptics/advanced_haptics.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

class FakeHaptics extends AdvancedHapticsPlatform with MockPlatformInterfaceMixin {
  final calls = <String>[];

  @override
  Future<bool> hasCustomHapticsSupport() async => true;

  @override
  Future<void> playWaveform({
    required List<int> timings,
    required List<int> amplitudes,
    int repeat = -1,
    double atTime = 0.0,
  }) async => calls.add('playWaveform');
  // Override the other methods you care about; the defaults throw UnimplementedError.
}

void main() {
  setUp(() => AdvancedHapticsPlatform.instance = FakeHaptics());
}

Without a fake, the plugin is still safe in flutter test: with no native implementation every call is a no-op and hasCustomHapticsSupport() returns false.


๐Ÿ™Œ Contributors #

Thanks to these wonderful people for their contributions:

miracle101000
miracle101000
kvenn
kvenn
rdeekshitha-scapia
rdeekshitha-scapia
KoichiMatsudaMPL
KoichiMatsudaMPL

We welcome issues, feature requests, and pull requests! If submitting code, please test on both Android and iOS where applicable and provide details on the devices used.

๐Ÿ“„ License #

This project is licensed under the MIT License. See the LICENSE file for full details.

5
likes
160
points
1.45k
downloads

Documentation

API reference

Publisher

verified publisherfendomoney.com

Weekly Downloads

A Flutter plugin for playing advanced, custom haptic feedback patterns on Android and iOS, including waveforms and Core Haptics .ahap files.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on advanced_haptics

Packages that implement advanced_haptics