Skip to content

Conversation

nicolasbraun
Copy link
Contributor

Note

This is the follow up of PR #789 that had become quite unreadable due to lots of testing and iteration.

Objectve

The goal of this PR is to provide UI support for the reactions, which are already in the model.

UI wise my goal was to have something in the Signal/Whatsapp style, as opposed to the iMessage / getStream.io one. That is:

  • A row of reactions under the message bubble
  • A long press on the message to display contextual menus around the bubble, with reactions and other contextuals actions. (note the package is maybe poorly named, as it does more than reactions, but this is the main focus)

I made i customizable so the client can chose the behavior on reaction tap, long press ...

Dev goals

I did my best to:

  • not rely on IntrinsicWidth to build the reactions
  • not make flutter_chat_ui depend on this package.

Integration

Row of reactions under the message

  • Added a new builder. The user can then user FlyerChatReactionsRow widget with any logic he might want.
          reactionsBuilder: (context, message, isSentByMe) {
            final reactions = reactionsFromMessageReactions(
              reactions: message.reactions,
              currentUserId: _currentUser.id,
            );
            return FlyerChatReactionsRow(
              reactions: reactions,
              alignment: isSentByMe
                  ? MainAxisAlignment.start
                  : MainAxisAlignment.end,

              /// Open List on tap (WhatsApp Style)
              // onReactionTap: (reaction) =>
              //     showReactionsList(context: context, reactions: reactions),
              /// Or react on tap (Slack Style)
              onReactionTap: (reaction) =>
                  _handleReactionTap(message, reaction),
              removeOrAddLocallyOnTap: true,
              onReactionLongPress: (reaction) =>
                  showReactionsList(context: context, reactions: reactions),
              onSurplusReactionTap: () =>
                  showReactionsList(context: context, reactions: reactions),
            );
          },

LongPress action
The idea is to let the user decide when he wants to display the dialog.
The package exposes a showReactionsDialog method

  void _handleMessageLongPress(
    BuildContext context,
    Message message, {
    int? index,
    LongPressStartDetails? details,
    required bool isSentByMe,
  }) async {
    showReactionsDialog(
      context,
      message,
      isSentByMe: isSentByMe,
      userReactions: getUserReactions(message.reactions, _currentUser.id),
      onReactionTap: (reaction) => _handleReactionTap(message, reaction), 
      onMoreReactionsTap: () async {  // <== Optional too pick another emoji than the default one. 
        // Use whichever emoji picker you want
        final picked = await _showEmojiPicker();
        if (picked != null) {
          _handleReactionTap(message, picked);
        }
      },
      menuItems: _getMenuItems(message), // <== Contextual actions
    );
  }

What the lib does NOT do

  • The library does not handle any logic to remove a previous reactions if a new one is added from the ChatController. It's up to the client do decide what to do. Some might want to allow multi react. Note the boolean removeOrAddLocallyOnTap allow to visually add/remove one on tap.
  • The lib does dot implement emoji picker for "more" reactions, it's up to the client to choose one, cf example

What I add to change in the main lib

  • Breaking: Add isSentByMe to some callbacks
  • Expose the _buildMessage method (to rebuild the "inner" bubble in the dialog)

Still todo?

  • Maybe some animation on the row, espacially on tap when removeOrAddLocallyOnTap is true; or at least a splash effect?
  • I wasn't particularly inspired for the list layout. We could add avatars

Video

Recording.at.2025-07-18.09.24.00.mp4

Shots

Web
image

MacOS

image

@nicolasbraun
Copy link
Contributor Author

@demchenkoalex maybe we should expose params as config object everywhere.
I did it for the list but for the reactionDialog / reactionssRow it may be a good thing too, it avoid copying parameter from the widget to the showXX method

@demchenkoalex
Copy link
Member

hey @nicolasbraun can't thank you enough for this! I just started reviewing (was working on quite a bit change for two-sided pagination, released in 2.9.0) and this looks amazing 🤩

will deep dive into code now to understand how it works and make changes if necessary. I am now paying for the bugbot from Cursor (just until Aug 10 to test) so will run as well to see what it says.

@demchenkoalex demchenkoalex force-pushed the feat/reactions_clean branch from 179f91a to 78ceecc Compare July 26, 2025 15:00
@demchenkoalex demchenkoalex requested a review from Copilot July 26, 2025 15:00
Copy link

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds comprehensive reaction support to the Flutter chat UI, following the Signal/WhatsApp style with a row of reactions under message bubbles and long-press contextual menus. The implementation creates a new flyer_chat_reactions package to provide this functionality without making the main UI library dependent on reactions.

Key changes:

  • New standalone package for reaction widgets and functionality
  • Breaking changes to callback signatures to include isSentByMe parameter
  • Support for both tap-to-react and tap-to-show-list behaviors

Reviewed Changes

Copilot reviewed 35 out of 37 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/flyer_chat_reactions/ New package providing reaction widgets, models, and helpers
packages/flutter_chat_ui/lib/src/utils/typedefs.dart Added isSentByMe parameter to message callback signatures
packages/flutter_chat_ui/lib/src/chat_message/chat_message.dart Integrated reactions builder and updated gesture handling
packages/flutter_chat_core/lib/src/models/message.dart Added MessageReactions typedef and updated message types
packages/flutter_chat_core/lib/src/theme/chat_theme.dart Added surfaceContainerHighest color for elevated surfaces
examples/flyer_chat/ Updated example to demonstrate reaction functionality

reactionsWidget != null
? Stack(
children: [
// TODO Find better way to add height for the reactions widget
Copy link
Preview

Copilot AI Jul 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The hardcoded SizedBox(height: 16) is a temporary solution for reactions spacing. This could cause layout issues with different reaction configurations or screen sizes. Consider using a more dynamic approach based on actual reaction widget height.

Copilot uses AI. Check for mistakes.

@demchenkoalex
Copy link
Member

@cursor review

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bugbot free trial expires on July 29, 2025
Learn more in the Cursor dashboard.

@demchenkoalex
Copy link
Member

Hey @nicolasbraun - after taking a closer look, I realised that the menu has also been recreated, and honestly, it doesn’t look great. It lacks animation, has no hover effects, and feels pretty static. Plus, it introduces another layer to maintain.

My long-term goal was to go fully native, similar to this:

Screen.Recording.2025-07-27.at.15.13.39.mov

(That package doesn’t currently support adding arbitrary content above the menu, like reactions, and I don’t have time right now to extend it myself.)

That native menu style would be ideal - WhatsApp does exactly that, using the native context menu with a custom widget above for reactions.

Since that’s not feasible at the moment, I think the second best option might be using something like family_bottom_sheet. It mimics the experience of apps like Slack or Discord, where both reactions and menus are presented in a unified bottom sheet. It’s inspired by Family, and I think it looks and feels really solid (I use it on https://flyer.chat).

So I’m thinking - maybe we could move reactions + the menu into a bottom sheet for a better UI experience?

The only possible downside is integration with the emoji picker - having two differently styled bottom sheets might feel weird. I’m not sure what level of customisation is available in the emoji picker, but if we can either embed it inside the family sheet or make it behave more like a keyboard (instead of a sheet), it should be fine.

I see a few options:

  1. Split the MR into two parts, and only implement the bits in chat needed to connect to your reactions package. You could then publish that under your own name/project - if that’s something you want to use.
  2. Go with the bottom sheet approach (reactions + menu), if you like the idea - I think this will result in a better user experience overall.
  3. If you’re not a fan of either of the above, I can try to improve the current menu - make it more “alive” with animations, better styling, etc. But that will definitely take more time.

Let me know your thoughts!

@nicolasbraun
Copy link
Contributor Author

Hello @demchenkoalex

What do you mean by full native and what is the package you refer to?
Whatsapp or Signal have an UX similar to mine that is the reactions above the bubble and a context menu below.
I did not want to depend on package but we could use pull_down_button you had in the example if it has a smoother UI, it's a quick fix.

Also where does the your video comes from? There seems to be a Hero animation, which I removed for now, it was causing top many layouts issues during page transition.

As per your bottom sheet proposition, why not, i leaned toward the current UX because that's the most common one in chat apps but Slack one is quite nice too. Regarding the picker we could simply pop the bottomSheet and let the use do as it pleases (same as I currently did, to no include the emoji package in our package).
Also I'm not sure we need Family, a simple bottomSheet seems it will be enough?

I think we can maybe:

  • go with proposition 1: only remaining point is the height for the reactions (for the stack to account for it) is currently hardcoded, maybe adding a param to overwrite this would be nice and it's quite easy
  • in parrallel we can work on improving the package (mostly the menu, but maybe also use a Class for the configuration of the reactionsRow, I feel it makes it more readable).

I will have less time in the upcoming weeks but if it's "only" switching to the pull_down_button lib and exposing some parameters and fixing cursor analysis it should be fine (also maybe we should come up with a better name for the lib which does not only do reactions but it was the primary purpose)

Let me know, we can chat on Discord sometimes (can email you my ID) if you wanna discuss this.

@demchenkoalex
Copy link
Member

Definitely, not sure if my email is visible anywhere, if not, you can send to [email protected]

@nicolasbraun
Copy link
Contributor Author

nicolasbraun commented Jul 29, 2025

hello @demchenkoalex

Made a test with pull_down_button and this works well: the lib exposes a method to draw the Menu, without handling the navigation.
So we have for free all pull_down_button feats like hover + the ability to use any PullDownMenuEntry

I'm quite a noob in animation but should we want to animate how the menu appears it should be quite easy?

The only drawback for me are

  • When defining the menu entries you have to explicitely call Navigator.pop() in the onTap so the dialog is poped
  • User as to import pull_down_button
  • The menu does not really rely on the chatTheme anymore, but that was already the case in you example anyway

There is one remaining conversation open + i think we should expose the params as a configuration objects, it will make the code more robust, will give it a try tomorrow.
Let me know what you think, feel free to hit me up on Discord, you should have gotten my ID.

@nicolasbraun nicolasbraun force-pushed the feat/reactions_clean branch from 77afe28 to 8b0c2e7 Compare July 30, 2025 09:14
@nicolasbraun
Copy link
Contributor Author

@demchenkoalex I'm done. I started working on Config classes but wanted to discuss it before to not pollute this PR.

My idea was for example a ReactionsDialogConfig which would take all the parameters related to layout / style, so the ReactionsDialogWidget and the showReactionDialog would require the callbacks and the config, a bit like the EmojiPicker lib does.
I had no strong opinion on what should be in the config or outside it (for example alignment which is quite common to use could be outside of it). It's fairly easy to implement once decided.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants