alt Banner of the core_haptics project

A cross-platform haptic feedback library for Flutter. Full Core Haptics power on iOS and macOS with automatic fallback to Flutter's HapticFeedback on Android and other platforms. Create custom vibration patterns, play AHAP files, and deliver tactile experiences that feel native.

โœจ What's inside

  • ๐ŸŒ Cross-platform โ€” works everywhere! Core Haptics on Apple, HapticFeedback fallback elsewhere
  • ๐ŸŽฏ Complete Core Haptics wrapper โ€” engines, patterns, players, and dynamic parameters (iOS/macOS)
  • โšก One-liner haptics โ€” HapticEngine.success(), HapticEngine.mediumImpact() with zero setup
  • ๐Ÿ“„ AHAP everywhere โ€” load from JSON strings, files, or Flutter assets (iOS/macOS)
  • ๐ŸŽจ Programmatic patterns โ€” build haptic sequences with HapticEvent (iOS/macOS)
  • ๐Ÿ›ก๏ธ Memory-safe FFI โ€” automatic cleanup with finalizers, strongly-typed enums
  • ๐ŸŽ›๏ธ Live parameter control โ€” adjust intensity and sharpness during playback (iOS/macOS)
  • ๐Ÿ”„ Interruption handling โ€” callbacks for audio session changes and resets (iOS/macOS)
  • ๐Ÿšซ Zero CocoaPods โ€” Swift Package Manager only, clean and modern

๐Ÿ“ฑ Platform Support

Feature iOS MacOS Android Windows Linux Web
lightImpact() โœ… โœ… โœ… โœ… โœ… โœ…
mediumImpact() โœ… โœ… โœ… โœ… โœ… โœ…
heavyImpact() โœ… โœ… โœ… โœ… โœ… โœ…
softImpact() โœ… โœ… โšก โšก โšก โšก
rigidImpact() โœ… โœ… โšก โšก โšก โšก
success() โœ… โœ… โšก โšก โšก โšก
warning() โœ… โœ… โšก โšก โšก โšก
error() โœ… โœ… โšก โšก โšก โšก
selection() โœ… โœ… โœ… โœ… โœ… โœ…
HapticEngine โœ… โœ… โŒ โŒ โŒ โŒ
AHAP patterns โœ… โœ… โŒ โŒ โŒ โŒ
Looping โœ… โœ… โŒ โŒ โŒ โŒ
Dynamic parameters โœ… โœ… โŒ โŒ โŒ โŒ

Legend: โœ… Full support | โšก Fallback (closest equivalent) | โŒ Not available

Note

On non-Apple platforms, the static methods automatically use Flutter's HapticFeedback service. Methods like softImpact() and success() map to the closest available feedback type.

๐Ÿ“ฆ Installation

Add to your pubspec.yaml:

dependencies:
  core_haptics: ^latest_version

Then run:

flutter pub get

๐Ÿ”ง Platform Setup

Android, Windows, Linux, Web

No setup required! The plugin automatically uses Flutter's built-in HapticFeedback service on these platforms.

iOS / macOS (Full Core Haptics)

The plugin uses Swift Package Manager to build the native module.

Requirements:

  • iOS 13.0+ or macOS 11.0+
  • Physical device with haptic engine (iPhone 8+ or supported Mac)

Recommended: Enable Flutter's SwiftPM support and everything works automatically:

flutter config --enable-swift-package-manager

That's it! Flutter handles linking the native module for you.

Manual setup (if you can't use the SwiftPM feature flag)

Step 1: Open your app in Xcode
example/ios/Runner.xcworkspace (or macos/Runner.xcworkspace)

Step 2: Add the local Swift Package

  • File โ†’ Add Package Dependencies
  • Click "Add Local..." and navigate to the plugin's ios/ folder
  • Select Package.swift and add it

Step 3: Link to your target

  • In your app target's Frameworks and Libraries, add CoreHapticsFFI
  • Set to Embed & Sign (iOS) or Do Not Embed (macOS)

Note

SwiftPM gives cleaner builds, better dependency management, and is Apple's recommended approach for modern Swift libraries.

๐Ÿš€ Quick Start

The easiest way to use the plugin is to use the static methods โ€” they work on all platforms! For advanced control (iOS/macOS only), create an engine instance.

One-liner haptics (Cross-platform)

All static methods work on every platform. On iOS/macOS they use Core Haptics; on Android and other platforms they automatically fall back to Flutter's HapticFeedback.

import 'package:core_haptics/core_haptics.dart';

// Impact feedback โ€” works everywhere!
await HapticEngine.lightImpact();
await HapticEngine.mediumImpact();
await HapticEngine.heavyImpact();

// Notification feedback โ€” native on iOS/macOS, fallback elsewhere
await HapticEngine.success();
await HapticEngine.warning();
await HapticEngine.error();

// Selection feedback โ€” works everywhere!
await HapticEngine.selection();

Use isSupported when you need to make UI decisions based on haptics availability:

// Show/hide haptics settings based on device capability
final showHapticsToggle = await HapticEngine.isSupported;

Check if advanced features (patterns, looping, etc.) are available:

// Only iOS/macOS support the full HapticEngine API
if (HapticEngine.supportsAdvancedHaptics) {
  // Can use HapticEngine.create(), patterns, etc.
}

For custom patterns with precise timing (iOS/macOS only):

// Note: This throws HapticsException on non-Apple platforms
await HapticEngine.play([
  HapticEvent(type: HapticEventType.transient, intensity: 0.8, sharpness: 0.5),
  HapticEvent(
    type: HapticEventType.continuous,
    time: Duration(milliseconds: 100),
    duration: Duration(seconds: 1),
    intensity: 0.5,
  ),
]);

Advanced usage (iOS/macOS only)

For full control over engines, patterns, players, looping, and dynamic parameters, use the HapticEngine API directly.

Caution

The advanced API requires Core Haptics and only works on iOS/macOS. On other platforms, HapticEngine.create() throws HapticsException(notSupported).

Basic haptic tap

import 'package:core_haptics/core_haptics.dart';

Future<void> playSimpleTap() async {
  // Create and start the engine
  final engine = await HapticEngine.create();
  await engine.start();

  // Load an AHAP pattern (JSON string)
  final pattern = await engine.loadPatternFromAhap('''
  {
    "Version": 1,
    "Pattern": [{
      "Event": {
        "EventType": "HapticTransient",
        "Time": 0,
        "EventParameters": [
          {"ParameterID": "HapticIntensity", "ParameterValue": 0.8},
          {"ParameterID": "HapticSharpness", "ParameterValue": 0.5}
        ]
      }
    }]
  }
  ''');

  // Play it
  final player = await engine.createPlayer(pattern);
  await player.play();
  
  // Cleanup
  await player.dispose();
  await pattern.dispose();
  await engine.dispose();
}

Programmatic patterns

final pattern = await engine.loadPatternFromEvents([
  // Sharp tap at start
  const HapticEvent(
    type: HapticEventType.transient,
    time: Duration.zero,
    intensity: 1.0,
    sharpness: 0.8,
  ),
  // Continuous rumble after 300ms
  const HapticEvent(
    type: HapticEventType.continuous,
    time: Duration(milliseconds: 300),
    duration: Duration(seconds: 2),
    intensity: 0.6,
    sharpness: 0.3,
  ),
]);

Load from Flutter assets

// 1. Add AHAP file to pubspec.yaml assets
// 2. Load it:
final pattern = await engine.loadPatternFromAsset('assets/haptics/my_pattern.ahap');

Handle interruptions

final engine = await HapticEngine.create(
  onEvent: (event, message) {
    switch (event) {
      HapticEngineEvent.interrupted => print('โš ๏ธ Haptics interrupted: $message');
      HapticEngineEvent.restarted => print('โœ… Haptics resumed');
    }
  },
);

๐ŸŽฏ API Reference

HapticEngine

Your main entry point. Provides both static one-liner methods (cross-platform) and full engine control (iOS/macOS).

Static methods (cross-platform, uses native feedback or Flutter fallback):

// Impact feedback โ€” works on all platforms
await HapticEngine.lightImpact();
await HapticEngine.mediumImpact();
await HapticEngine.heavyImpact();
await HapticEngine.softImpact();  // Falls back to light on non-Apple
await HapticEngine.rigidImpact(); // Falls back to heavy on non-Apple

// Notification feedback โ€” falls back on non-Apple platforms
await HapticEngine.success();  // mediumImpact on non-Apple
await HapticEngine.warning();  // heavyImpact on non-Apple
await HapticEngine.error();    // vibrate on non-Apple

// Selection feedback โ€” works on all platforms
await HapticEngine.selection();

// Custom patterns (iOS/macOS only โ€” throws on other platforms)
await HapticEngine.play(eventList);

// Check device support (for UI decisions)
if (await HapticEngine.isSupported) { ... }

// Check if advanced features are available
if (HapticEngine.supportsAdvancedHaptics) { ... }

Instance API (iOS/macOS only):

// Create โ€” throws `HapticsException` on non-Apple platforms
final engine = await HapticEngine.create(onEvent: callback);

// Start/stop
await engine.start();
await engine.stop();

// Load patterns
final p1 = await engine.loadPatternFromAhap(jsonString);
final p2 = await engine.loadPatternFromFile(path);
final p3 = await engine.loadPatternFromAsset('assets/pattern.ahap');
final p4 = await engine.loadPatternFromEvents(eventList);

// Create players
final player = await engine.createPlayer(pattern);

// Cleanup
await engine.dispose();

HapticPlayer (iOS/macOS only)

Controls playback of a haptic pattern.

await player.play(atTime: 0);
await player.stop(atTime: 0);

// Looping
await player.setLoop(enabled: true, loopStart: 0, loopEnd: 2.0);

// Dynamic parameter updates (during playback)
await player.setParameter(HapticParameterId.hapticIntensity, 0.9, atTime: 0);

await player.dispose();

HapticEvent

Programmatically define haptic events.

const HapticEvent({
  required HapticEventType type,      // transient or continuous
  Duration time = Duration.zero,      // when to fire (relative to pattern start)
  Duration? duration,                 // required for continuous events
  double? intensity,                  // 0.0 to 1.0
  double? sharpness,                  // 0.0 to 1.0
});

Error Handling

All errors throw HapticsException:

try {
  await engine.start();
} on HapticsException catch (e) {
  print('Error: ${e.code} - ${e.message}');
  // e.code is a HapticsErrorCode enum
}

Error codes:

Code Description
notSupported Device doesn't support haptics
engine Engine failed to start
invalidArgument Bad pattern or parameter
decode Invalid AHAP JSON
io File not found
runtime Playback issue

๐Ÿงช Testing

Dart unit tests:

flutter test

Uses mocked FFI bridge for ~90% API coverage.

Swift native tests:

cd ios && swift test

Tests the Core Haptics bridge (skips on devices without haptics).

๐Ÿ› Troubleshooting

"HapticsException, notSupported"

You're trying to use HapticEngine.create(), HapticEngine.play(), or other advanced features on a non-Apple platform. These features require Core Haptics and only work on iOS and macOS. Use the static methods (lightImpact(), success(), etc.) for cross-platform haptics.

"Cannot find symbol 'chffi_engine_create'" (iOS/macOS)

The SwiftPM package isn't linked. Go back to Platform Setup and ensure CoreHapticsFFI is added to your app target's frameworks.

"Device does not support haptics" (iOS/macOS)

Core Haptics requires an iPhone 8+ or newer Mac with Taptic Engine. Simulators don't support haptics.

"HapticsException: runtime (-4820)" (iOS/macOS)

Tried to send a parameter update to a player that isn't actively playing. Ensure the pattern is playing before calling setParameter.

๐Ÿ“š Learn More

๐Ÿ“„ License

See LICENSE for details.

๐Ÿ™Œ Contributing

Issues and PRs welcome! This plugin maintains a 1:1 mapping with Core Haptics APIs, so contributions should align with Apple's framework design.

Libraries

core_haptics