Reaction Overlay

A floating reaction bar for Flutter, inspired by WhatsApp and iMessage. It anchors to any widget and provides quick reactions, an emoji picker, adaptive positioning, accessibility, and light/dark theme support.

Installation

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

Quick start

Wrap a message, or any widget, with ReactionTarget. Long press it to open the reaction bar.

ReactionTarget(
  reactions: [
    Reaction('👍', label: 'Like', onTap: () => saveReaction('👍')),
    Reaction('❤️', label: 'Love', onTap: () => saveReaction('❤️')),
    Reaction('😂', label: 'Funny', onTap: () => saveReaction('😂')),
  ],
  child: const MessageBubble(text: 'Hello'),
)

ReactionTarget does not require a GlobalKey: it uses the widget's BuildContext to calculate its position.

WhatsApp-style double tap and full bar

Set quickReactionIndex to react immediately on double tap. Long press still opens the full reaction picker.

ReactionTarget(
  reactions: ReactionSet.standard.reactions,
  quickReactionIndex: 0, // 👍
  onReactionChanged: (index, reaction) {
    saveReaction(reaction.emoji!);
  },
  child: MessageBubble(text: message.text),
)

If you do not set quickReactionIndex, TriggerMode.doubleTap opens the full reaction bar instead.

Centralized configuration

Use ReactionOverlayConfig when you reuse the same configuration or want to keep ReactionOverlay.show calls readable.

final messageReactions = ReactionOverlayConfig(
  reactions: ReactionSet.social.reactions,
  position: OverlayPosition.auto,
  maxVisibleButtons: 6,
  dimBackground: true,
  showExpandButton: true,
  recentEmojis: const ['👍', '❤️', '🙏'],
  enableDragPreview: true,
  enableSelectionParticles: false,
);

ReactionOverlay.show(
  context: context,
  config: messageReactions,
  onReactionChanged: (index, reaction) {
    saveReaction(reaction.emoji!);
  },
  onDismissed: () => debugPrint('Reaction picker closed'),
);

Do not mix config with individual styling parameters in ReactionOverlay.show; use the centralized configuration as the source of truth for those values. Callbacks passed to the method (onDismissed, onReactionChanged, and onEmojiSelected) can be supplied normally.

Common configuration options

Option Default Purpose
reactions required The list of Reaction objects shown in the bar.
position OverlayPosition.auto Position: auto, above, below, left, or right.
maxVisibleButtons 6 Maximum before horizontal navigation appears.
showExpandButton true Opens the full emoji panel.
recentEmojis [] Emojis shown first in the full panel.
dimBackground / dimColor true / 20% black Controls the background behind the bar.
enableDragPreview true Magnifies the reaction under the finger while dragging.
enableSelectionParticles true Shows particles on selection; disable for a quieter style.
allowMultipleSelections false Keeps the picker open after a selection.

Buttons keep a minimum 48×48 px tap target even if buttonSize or buttonHeight is smaller. The package also respects the system's reduce-motion preference.

Show the overlay manually

Use ReactionOverlay.show when you provide your own gesture. The context must belong to a widget that has already been laid out.

GestureDetector(
  onTap: () {
    ReactionOverlay.show(
      context: context,
      reactions: [Reaction('👍'), Reaction('❤️')],
      position: OverlayPosition.above,
      onReactionChanged: (index, reaction) => saveReaction(reaction.emoji!),
    );
  },
  child: const MessageBubble(text: 'Tap me'),
)

Custom reactions and presets

final reactions = [
  Reaction('👍', label: 'Like'),
  Reaction.custom(
    child: const Icon(Icons.star, color: Colors.amber),
    label: 'Favorite',
  ),
];

// Presets: standard, social, work, gaming, and minimal.
final socialReactions = ReactionSet.social.reactions;

Global theme

Configure visual values that apply to all ReactionTarget widgets.

MaterialApp(
  theme: ThemeData(
    extensions: const [
      ReactionOverlayThemeData(
        position: OverlayPosition.above,
        enableHapticFeedback: true,
        buttonSize: 44,
        buttonHeight: 48,
      ),
    ],
  ),
  home: const ChatPage(),
)

Properties passed directly to ReactionTarget take precedence over this theme.

Programmatic control

ReactionOverlayController lets you inspect visibility and the most recently selected reaction.

final controller = ReactionOverlayController();

controller.show(
  context: context,
  reactions: ReactionSet.standard.reactions,
);

// controller.isVisible
// controller.selectedReaction
controller.dismiss();

// Call dispose when the controller is no longer needed.
controller.dispose();

Migrating from 0.0.3

Version 0.1.0 simplifies the API and removes manual key management.

In 0.0.3 In 0.1.0
AppReactionOverlayManager ReactionOverlay.show, ReactionTarget, or ReactionOverlayController.
itemKey / itemId Remove these parameters; pass the BuildContext to ReactionOverlay.show or use ReactionTarget.
List<IconButton> buttons List<Reaction> reactions.
Manually building the overlay ReactionTarget for the usual case.

1. Replace IconButton with Reaction

Previously, reactions were built with IconButton widgets. Use the simpler model now:

// 0.0.3
final buttons = [
  IconButton(icon: const Icon(Icons.thumb_up), onPressed: onLike),
];

// 0.1.0
final reactions = [
  Reaction('👍', label: 'Like', onTap: onLike),
];

To keep a custom icon, use Reaction.custom:

Reaction.custom(
  child: const Icon(Icons.thumb_up),
  label: 'Like',
  onTap: onLike,
)

2. Remove GlobalKey, itemKey, and itemId

Instead of managing position through a key, wrap the content:

// 0.1.0
ReactionTarget(
  reactions: reactions,
  child: MessageBubble(text: message.text),
)

If you need to open the picker from your own gesture, call ReactionOverlay.show with that widget's context, as shown above.

3. Replace the manager

Replace AppReactionOverlayManager with one of these options:

  • ReactionTarget: recommended declarative integration.
  • ReactionOverlay.show: when you already have a gesture callback.
  • ReactionOverlayController: when another part of your app needs to close, toggle, or observe the overlay.

Accessibility and interaction

  • Long press opens the reaction bar; quick double tap is optional.
  • Left/right arrows navigate, Enter/Space selects, and Escape closes.
  • Give every Reaction a label so screen readers announce a useful action.
  • Use onReactionChanged to synchronize a selection with your backend.

Demo

See the example directory for a demo application.

Reaction Overlay demo

Author

GitHub: Ignaciomanchu1998

Libraries

reaction_overlay