Feat/reactions
[!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
IntrinsicWidthto build the reactions - not make
flutter_chat_uidepend on this package.
Integration
Row of reactions under the message
- Added a new builder. The user can then user
FlyerChatReactionsRowwidget 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 booleanremoveOrAddLocallyOnTapallow 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
isSentByMeto some callbacks - Expose the
_buildMessagemethod (to rebuild the "inner" bubble in the dialog)
Still todo?
- Maybe some animation on the row, espacially on tap when
removeOrAddLocallyOnTapis true; or at least a splash effect? - I wasn't particularly inspired for the list layout. We could add avatars
Video
https://github.com/user-attachments/assets/0620792d-e083-4d46-882f-decc85b51746
Shots
Web
MacOS
@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
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.
@cursor review
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:
https://github.com/user-attachments/assets/e17421ec-fa79-4c87-818f-891b73bfc7e7
(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:
- 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.
- Go with the bottom sheet approach (reactions + menu), if you like the idea - I think this will result in a better user experience overall.
- 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!
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.
Definitely, not sure if my email is visible anywhere, if not, you can send to [email protected]
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 theonTapso 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.
@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.
why nobody has merge it ?
why nobody has merge it ?
I'm only merging MRs that I'm 100% confident in. Even though the solution works, there are some weird jumps and no animations, etc - I need more time to polish this one. Unfortunately, there was a restructure at my day job, and for the past few months I've been working extra hard with no time left for open source. So, it'll get merged once I'm back and can make the changes to perfect it.