instagram_like_animation_button 4.0.0 copy "instagram_like_animation_button: ^4.0.0" to clipboard
instagram_like_animation_button: ^4.0.0 copied to clipboard

Fully customizable Instagram Reels double-tap heart animation for Flutter. Fly-to-target, custom builders, gradients, haptic control.

📱 Instagram Like Animation Button #

pub package License: MIT Flutter

A fully customizable, OOP-based Flutter package that replicates the Instagram Reels double-tap heart animation — the heart pops up at the tap position, rotates, and flies to a target widget.

Demo


✨ Features #

Feature Description
🎯 Fly-to-Target Heart flies from double-tap position to any target widget
🎨 Custom Gradients Use any gradient on the heart icon
🔧 Custom Widget Builder Replace the heart with your own widget (emoji, icon, lottie, etc.)
📳 Haptic Control Enable/disable/change haptic feedback type (light, medium, heavy, none)
Animation Config Customize duration, curves, scale, rotation, timing — 16 parameters
🎭 Full OOP Mixins, extensions, models, services — clean architecture
📦 Zero Dependencies Only depends on Flutter SDK
🔁 DoubleTapDetector Built-in gesture handler with position data

🚀 Getting Started #

Installation #

Add to your pubspec.yaml:

dependencies:
  instagram_like_animation_button: ^4.0.0

Or with a Git reference:

dependencies:
  instagram_like_animation_button:
    git:
      url: https://github.com/iambhabha/instagram_like_animation_button.git

Then run:

flutter pub get

Import #

import 'package:instagram_like_animation_button/instagram_like_animation_button.dart';

📖 Quick Start #

class _MyPageState extends State<MyPage> {
  final GlobalKey likeKey = GlobalKey();
  bool isLiked = false;
  TapDownDetails? tapDetails;

  @override
  Widget build(BuildContext context) {
    return DoubleTapDetector(
      onDoubleTap: (details) => setState(() => tapDetails = details),
      child: Stack(
        children: [
          // Your content (image, video, reels, etc.)
          Container(color: Colors.black),

          // Target icon with GlobalKey
          InkWell(
            key: likeKey,
            onTap: () => setState(() => isLiked = !isLiked),
            child: Icon(
              Icons.favorite,
              color: isLiked ? Colors.red : Colors.white,
            ),
          ),

          // Animation layer
          if (tapDetails != null)
            ReelAnimationLike(
              key: ValueKey(tapDetails),
              likeKey: likeKey,
              position: tapDetails!.globalPosition,
              onLikeCall: () => setState(() => isLiked = true),
              onCompleteAnimation: () => setState(() => tapDetails = null),
            ),
        ],
      ),
    );
  }
}

🏗️ Architecture #

lib/
├── instagram_like_animation_button.dart    ← Barrel export (single import)
└── src/
    ├── core/
    │   ├── enums.dart                      ← HapticFeedbackType
    │   └── typedefs.dart                   ← AnimationWidgetBuilder, callbacks
    ├── config/
    │   └── like_animation_config.dart      ← 16 configurable animation params
    ├── mixins/
    │   └── haptic_feedback_mixin.dart      ← Haptic feedback mixin
    ├── extensions/
    │   ├── global_key_extensions.dart      ← GlobalKey.centerPosition, etc.
    │   └── offset_extensions.dart          ← Offset.deltaTo()
    ├── models/
    │   └── like_animation_style.dart       ← Gradient, color, icon size
    ├── resources/
    │   ├── asset_res.dart                  ← Asset path constants
    │   ├── color_res.dart                  ← Color constants
    │   └── style_res.dart                  ← Theme gradient
    ├── services/
    │   ├── haptic_manager.dart             ← Singleton haptic service
    │   └── debounce_action.dart            ← Singleton debounce service
    └── widgets/
        ├── reel_animation_like.dart        ← Main animation widget ⭐
        ├── heart_widget.dart               ← Default heart icon
        ├── gradient_icon.dart              ← Gradient ShaderMask wrapper
        └── double_tap_detector.dart        ← Double-tap gesture handler

📚 API Reference #

ReelAnimationLike — Main Widget ⭐ #

The core widget. Place in a Stack — it animates from positionlikeKey target.

Parameter Type Required Description
likeKey GlobalKey Target widget key
position Offset Tap position (global)
config LikeAnimationConfig Animation parameters
style LikeAnimationStyle Visual style
builder AnimationWidgetBuilder? Custom widget builder
onLikeCall VoidCallback? Called on like
onCompleteAnimation VoidCallback? Called on animation end
leftRightPosition double X offset adjustment
topBottomPosition double Y offset adjustment
ReelAnimationLike(
  likeKey: targetKey,
  position: tapPosition,
  config: const LikeAnimationConfig(
    duration: Duration(milliseconds: 800),
    hapticType: HapticFeedbackType.medium,
    scaleMax: 2.0,
  ),
  style: const LikeAnimationStyle(
    gradient: LinearGradient(colors: [Colors.pink, Colors.orange]),
    iconSize: Size(60, 60),
  ),
  onLikeCall: () => print('Liked!'),
)

Important

🎯 Position Fine-Tuning — leftRightPosition & topBottomPosition #

If the animation's starting position doesn't look accurate (heart appears slightly off from the tap point), you can adjust it using these two parameters:

Parameter What It Does When to Use
leftRightPosition Shifts the animation horizontally (X-axis) Heart appears too far left/right from tap
topBottomPosition Shifts the animation vertically (Y-axis) Heart appears too high/low from tap

Why is this needed? Every app has different layouts — AppBar, BottomNavigationBar, SafeArea, padding, etc. These affect where the tap coordinates land vs where the animation renders in the Stack. These offsets let you compensate for that difference.

How to use:

ReelAnimationLike(
likeKey: likeKey,
position: tapDetails!.globalPosition,

// 👇 Adjust these values until the heart appears exactly at the tap point
leftRightPosition: 8,    // shift 8px to the left
topBottomPosition: 65,   // shift 65px up (e.g., AppBar height)

onLikeCall: () {},
)

Quick guide:

  • topBottomPosition: 56 → standard AppBar height
  • topBottomPosition: 80 → AppBar + status bar
  • leftRightPosition: 0 → no horizontal adjustment needed (most cases)

💡 Tip: Start with 0 for both, double-tap on screen, and see where the heart appears. Then adjust the values until it lands exactly on your finger tap position.


LikeAnimationConfig — Animation Parameters #

Immutable config with copyWith() support. 16 customizable properties:

Property Type Default Description
duration Duration 600ms Total animation duration
scaleBegin double 0.0 Starting scale
scaleMax double 1.5 Peak scale (pop size)
scaleEnd double 0.7 Final scale
scaleCurveUp Curve easeOut Scale-up curve
scaleCurveDown Curve bounceIn Scale-down curve
scaleUpWeight double 30 Scale-up timing weight
scaleDownWeight double 100 Scale-down timing weight
initialOffset Offset (0, -50) Offset from tap
holdWeight double 50 Hold phase weight
flyWeight double 50 Fly phase weight
flyCurve Curve easeInOut Flight curve
rotationRange double 0.5 Max rotation (radians)
hapticType HapticFeedbackType light Haptic feedback type
fadeDuration Duration 500ms Fade-out duration
completionDelay Duration 500ms Delay before onComplete
// Disable haptic, fast animation
const config = LikeAnimationConfig(
  duration: Duration(milliseconds: 400),
  hapticType: HapticFeedbackType.none,
  scaleMax: 2.0,
);

// Modify existing config
final modified = config.copyWith(hapticType: HapticFeedbackType.heavy);

LikeAnimationStyle — Visual Style #

Property Type Default Description
gradient Gradient? theme gradient Gradient overlay
iconColor Color? #FF2D55 Icon tint color
iconSize Size 80×80 Icon dimensions
const style = LikeAnimationStyle(
  gradient: LinearGradient(colors: [Colors.purple, Colors.blue]),
  iconColor: Colors.pink,
  iconSize: Size(50, 50),
);

DoubleTapDetector — Gesture Handler #

Detects double-taps with position data (Flutter's onDoubleTap doesn't provide this).

DoubleTapDetector(
  onDoubleTap: (details) => print(details.globalPosition),
  onTap: () => print('Single tap'),
  behavior: HitTestBehavior.opaque,
  child: YourContent(),
)

HapticFeedbackType — Enum #

Value Description
none ❌ No haptic
light 💫 Light tap (default)
medium 📳 Medium tap
heavy 💥 Heavy tap
selection 🔘 Selection click
vibrate 📱 Standard vibration

HeartWidget — Default Icon #

HeartWidget(
  style: LikeAnimationStyle(iconColor: Colors.pink, iconSize: Size(60, 60)),
)

GradientIcon — Gradient Wrapper #

GradientIcon(
  gradient: LinearGradient(colors: [Colors.red, Colors.orange]),
  child: Icon(Icons.favorite, size: 48),
)

Extensions #

// GlobalKeyX — on GlobalKey
final Offset? pos    = myKey.globalPosition;    // top-left
final Offset? center = myKey.centerPosition;    // center
final Size?   size   = myKey.widgetSize;         // size

// OffsetX — on Offset
final delta = tapPosition.deltaTo(targetPosition);

Services #

// HapticManager (singleton)
HapticManager.instance.trigger(HapticFeedbackType.light);
HapticManager.instance.light();
HapticManager.instance.medium();

// DebounceAction (singleton)
DebounceAction.instance.call(() => print('Done'), milliseconds: 500);
DebounceAction.instance.cancel();

Resources #

AssetRes.icFillHeart    // 'assets/ic_fill_heart.png'
AssetRes.icHeart        // 'assets/ic_heart.png'

ColorRes.likeRed        // #FF2D55
ColorRes.whitePure      // #FFFFFF
ColorRes.blackPure      // #000000

StyleRes.themeGradient   // Default LinearGradient

🧩 Custom Widget Builder #

Replace the default heart with any widget — emoji, Lottie, custom icon:

ReelAnimationLike(
  likeKey: likeKey,
  position: tapPosition,
  builder: (context, scale, rotation, opacity) {
    return Opacity(
      opacity: opacity,
      child: Transform.scale(
        scale: scale.value,
        child: Transform.rotate(
          angle: rotation.value,
          child: Text('🔥', style: TextStyle(fontSize: 60)),
        ),
      ),
    );
  },
  onLikeCall: () {},
)

🎮 Full Example #

See the complete working example in example/lib/main.dart.

import 'package:flutter/material.dart';
import 'package:instagram_like_animation_button/instagram_like_animation_button.dart';

class ReelPage extends StatefulWidget {
  const ReelPage({super.key});

  @override
  State<ReelPage> createState() => _ReelPageState();
}

class _ReelPageState extends State<ReelPage> {
  final GlobalKey likeKey = GlobalKey();
  bool isLiked = false;
  TapDownDetails? tapDetails;

  @override
  Widget build(BuildContext context) {
    return DoubleTapDetector(
      onDoubleTap: (details) => setState(() => tapDetails = details),
      child: Stack(
        children: [
          Container(color: Colors.black), // Your video/image

          // Right-side icons (like Instagram Reels)
          Positioned(
            right: 16,
            bottom: 100,
            child: InkWell(
              key: likeKey,
              onTap: () => setState(() => isLiked = !isLiked),
              child: Image.asset(
                isLiked ? AssetRes.icFillHeart : AssetRes.icHeart,
                width: 28, height: 28,
                color: isLiked ? ColorRes.likeRed : ColorRes.whitePure,
                package: 'instagram_like_animation_button',
              ),
            ),
          ),

          // Animation
          if (tapDetails != null)
            ReelAnimationLike(
              key: ValueKey(tapDetails),
              likeKey: likeKey,
              position: tapDetails!.globalPosition,
              config: const LikeAnimationConfig(
                hapticType: HapticFeedbackType.light,
              ),
              style: const LikeAnimationStyle(iconSize: Size(50, 50)),
              onLikeCall: () {
                if (!isLiked) setState(() => isLiked = true);
              },
              onCompleteAnimation: () => setState(() => tapDetails = null),
            ),
        ],
      ),
    );
  }
}

📋 Requirements #

Version
Flutter ≥1.17.0
Dart ^3.10.7

📄 License #

MIT License — see LICENSE for details.

🤝 Contributing #

Contributions are welcome! Please open an issue or submit a pull request.


Made with ❤️ by @iambhabha

1
likes
145
points
40
downloads
screenshot

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Fully customizable Instagram Reels double-tap heart animation for Flutter. Fly-to-target, custom builders, gradients, haptic control.

Topics

#animation #instagram #like-button #ui #widget

License

MIT (license)

Dependencies

flutter

More

Packages that depend on instagram_like_animation_button