add reaction picker

This commit is contained in:
Salvatore Giordano
2020-09-01 15:09:47 +02:00
parent 0997867944
commit 8c17b268ae
13 changed files with 247 additions and 301 deletions
+5
View File
@@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.67671 21.168L5.89671 21.5206C4.02881 22.365 3.07001 22.562 3.02001 22.1116C2.96201 21.5883 2.93741 19.6566 2.94601 16.3168C2.95671 14.1008 2.97401 11.8946 2.99801 9.6981C3.04401 9.4089 3.40661 9.1108 4.08581 8.8038C4.75441 8.5015 5.17971 8.4179 5.36181 8.5529C5.41241 8.6067 5.45321 8.7736 5.48421 9.0535C5.60831 10.1733 5.63801 13.0549 5.57321 17.6982C5.56351 18.2138 5.56041 18.6882 5.56381 19.1212L8.40521 17.76C8.68381 17.6597 8.88741 17.737 9.01601 17.992C9.14461 18.247 9.22031 18.478 9.24331 18.6849C9.32561 19.4273 9.10871 19.9344 8.59281 20.206L8.06951 20.4617C7.56411 20.7285 7.09981 20.9639 6.67671 21.168Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.127 18.7617C10.868 19.2669 10.0196 19.0784 9.58195 18.1963C9.14425 17.3141 8.86335 16.3132 8.73925 15.1934C8.68255 14.6822 8.64835 14.1225 8.63665 13.5143C8.58155 11.6092 8.78765 9.8992 9.25495 8.3843C9.72225 6.8693 10.5819 5.8288 11.8342 5.2627L11.8661 5.2483C13.1278 4.7674 13.9792 4.9577 14.4201 5.8192C14.8611 6.6807 15.1417 7.6531 15.2617 8.7364C15.3211 9.2719 15.352 9.8523 15.3544 10.4775C15.4818 14.9445 14.406 17.7059 12.127 18.7617ZM12.0506 16.1121C12.6608 15.7852 12.9355 14.2422 12.8749 11.4833C12.8643 10.7851 12.828 10.1561 12.766 9.5962C12.7295 9.2676 12.6601 8.842 12.5576 8.3196C12.4551 7.7971 12.23 7.5762 11.8823 7.6567C11.5427 7.8102 11.311 8.3335 11.1871 9.2267C11.0632 10.1199 11.0129 11.1236 11.0361 12.2379C11.048 12.9482 11.0849 13.5834 11.147 14.1433C11.1848 14.4841 11.2579 14.9175 11.3664 15.4437C11.4749 15.9698 11.7029 16.1926 12.0506 16.1121Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.0009 15.1448L19.2209 15.4974C17.3531 16.3417 16.3942 16.5387 16.3443 16.0884C16.2863 15.565 16.2616 13.6334 16.2703 10.2936C16.281 8.0776 16.2983 5.8713 16.3223 3.6749C16.3682 3.3857 16.7308 3.0876 17.41 2.7805C18.0786 2.4783 18.504 2.3947 18.6861 2.5296C18.7366 2.5835 18.7774 2.7503 18.8085 3.0303C18.9326 4.1501 18.9623 7.0316 18.8975 11.675C18.8878 12.1906 18.8846 12.6649 18.8881 13.098L21.7295 11.7368C22.0081 11.6364 22.2117 11.7137 22.3402 11.9687C22.4688 12.2238 22.5446 12.4547 22.5675 12.6617C22.6498 13.4041 22.433 13.9111 21.917 14.1827L21.3937 14.4385C20.8883 14.7053 20.4241 14.9407 20.0009 15.1448Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.0001 20.9998C12.9077 21.0335 19.2333 16.9488 21.1353 12.8545C22.2893 10.3701 22.2882 8.06058 21.132 6.3507C20.1364 4.87861 18.3962 4 16.4777 4C13.7118 4 12.575 6.68418 12.0001 6.68418C11.4251 6.68418 10.4221 4 7.52265 4C5.6035 4 3.86348 4.87882 2.8679 6.3507C1.71166 8.06036 1.71077 10.3701 2.86479 12.8548C4.76705 16.9484 11.0924 20.9661 12.0001 20.9998Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 528 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 7.68147C5 8.9231 5.08724 10.7133 5.39357 11.2108C5.6999 11.7083 8.37046 14.1723 9.23394 16.1789C10.0974 18.1855 10.2463 21 11.6645 21C13.0826 21 13.5471 19.927 13.3966 17.7228C13.2462 15.5187 12.1925 14.1583 12.9335 13.6215C13.437 13.2567 14.3252 13.8533 16.4795 13.8533C17.418 13.8533 19 13.8533 19 12.5951C19 11.337 18.7229 11.4814 18.5964 10.6273C18.47 9.77329 18.8508 9.35923 18.4302 8.39561C18.0095 7.432 17.4917 7.31376 17.2509 6.62367C17.0102 5.93359 17.0945 5.19169 15.9684 4.1806C14.8422 3.1695 13.0448 3 11.6645 3C10.2842 3 6.18342 3.46243 5.61863 4.76815C5.05383 6.07387 5 6.54983 5 7.68147Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 774 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M5 16.3185C5 15.0769 5.08724 13.2867 5.39357 12.7892C5.6999 12.2917 8.37046 9.82769 9.23394 7.8211C10.0974 5.81451 10.2463 3 11.6645 3C13.0826 3 13.5471 4.07299 13.3966 6.27715C13.2462 8.48132 12.1925 9.8417 12.9335 10.3785C13.437 10.7433 14.3252 10.1467 16.4795 10.1467C17.418 10.1467 19 10.1467 19 11.4049C19 12.663 18.7229 12.5186 18.5964 13.3727C18.47 14.2267 18.8508 14.6408 18.4302 15.6044C18.0095 16.568 17.4917 16.6862 17.2509 17.3763C17.0102 18.0664 17.0945 18.8083 15.9684 19.8194C14.8422 20.8305 13.0448 21 11.6645 21C10.2842 21 6.18342 20.5376 5.61863 19.2318C5.05383 17.9261 5 17.4502 5 16.3185Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 777 B

+3
View File
@@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.1245 16.8091C18.2779 16.7951 18.4082 16.7392 18.5156 16.6414C18.6229 16.5437 18.6766 16.4179 18.6766 16.2643C18.7533 15.4261 18.9373 14.1444 19.2287 12.4191C19.5201 10.6938 19.8345 9.01397 20.1719 7.37951C20.5092 5.74505 20.7776 4.63446 20.977 4.04773C20.9923 3.99185 21 3.93597 21 3.88009C21 3.75437 20.9463 3.62864 20.839 3.50291C20.5322 3.19558 20.2524 3.03492 19.9993 3.02095C19.7463 3.00698 19.5814 3 19.5047 3C19.1673 3 18.8376 3.04191 18.5156 3.12573C18.1168 3.23749 17.8024 3.34924 17.5724 3.461C17.3577 3.54482 17.235 3.6915 17.2043 3.90105C17.097 4.59953 16.9091 5.85681 16.6407 7.67288C16.3723 9.48894 16.1615 11.078 16.0081 12.44C15.8548 13.8021 15.7781 14.7555 15.7781 15.3003C15.7781 15.6636 15.8011 15.922 15.8471 16.0757C16.0311 16.369 16.3455 16.5646 16.7903 16.6624C17.235 16.7602 17.6031 16.8091 17.8945 16.8091H18.1245ZM9.16441 16.369C9.01872 16.5367 8.83852 16.6275 8.62382 16.6414C8.3171 16.6414 8.00654 16.603 7.69215 16.5262C7.37776 16.4494 7.1899 16.2922 7.12855 16.0547C7.08254 15.8452 7.05954 15.5448 7.05954 15.1537C7.05954 14.9721 7.07104 14.6473 7.09405 14.1793C7.11705 13.7113 7.20523 13.135 7.35859 12.4505C7.51195 11.766 7.74966 11.2771 8.07172 10.9837C8.3171 10.8021 8.59314 10.6065 8.89986 10.397C9.66667 9.922 10.3415 9.3702 10.9242 8.74156C11.3076 8.33644 11.4993 7.89639 11.4993 7.42142C11.4993 7.156 11.438 6.88359 11.3153 6.60419C11.2079 6.33877 11.0469 6.15017 10.8322 6.03842C10.6328 5.92666 10.3415 5.87078 9.95805 5.87078H9.84303C8.46279 5.94063 7.61931 6.34575 7.31258 7.08615C7.20523 7.35157 7.15156 7.60303 7.15156 7.84051C7.15156 8.30151 7.32792 8.73458 7.68065 9.1397C7.788 9.26543 7.84168 9.39115 7.84168 9.51688C7.84168 9.55879 7.82251 9.63911 7.78417 9.75786C7.74583 9.8766 7.62697 9.97788 7.4276 10.0617C7.05954 10.1874 6.69914 10.2503 6.34641 10.2503C5.90167 10.2503 5.41475 10.1036 4.88566 9.81024C4.35656 9.51688 4.06134 8.86729 4 7.86147V7.77765C4 7.07916 4.2032 6.35972 4.60961 5.61932C5.01601 4.87893 5.69463 4.25728 6.64547 3.75437C7.50429 3.30733 8.61615 3.08382 9.98106 3.08382H10.3491C11.5913 3.12573 12.6457 3.52037 13.5122 4.26775C14.3787 5.01513 14.8656 5.94761 14.9729 7.06519V7.19092C14.9729 8.05704 14.6662 8.7695 14.0528 9.32829C13.4393 9.88708 12.7492 10.369 11.9824 10.7742C11.0929 11.2352 10.4795 11.7241 10.1421 12.241C9.86604 12.7718 9.65133 13.3586 9.49797 14.0012C9.39062 14.4342 9.33694 14.8463 9.33694 15.2375C9.33694 15.4331 9.35228 15.6286 9.38295 15.8242V15.8661C9.38295 16.0338 9.3101 16.2014 9.16441 16.369ZM9.60149 20.4657C9.21042 20.8219 8.7465 21 8.20974 21C7.65765 21 7.18606 20.8219 6.79499 20.4657C6.40392 20.1094 6.20839 19.6868 6.20839 19.1979C6.20839 18.695 6.40392 18.2654 6.79499 17.9092C7.18606 17.553 7.65765 17.3749 8.20974 17.3749C8.7465 17.3749 9.21042 17.553 9.60149 17.9092C9.99256 18.2654 10.1881 18.695 10.1881 19.1979C10.1881 19.6868 9.99256 20.1094 9.60149 20.4657ZM18.32 20.4657C17.929 20.8219 17.4574 21 16.9053 21C16.3685 21 15.9046 20.8219 15.5135 20.4657C15.1225 20.1094 14.9269 19.6868 14.9269 19.1979C14.9269 18.695 15.1225 18.2654 15.5135 17.9092C15.9046 17.553 16.3685 17.3749 16.9053 17.3749C17.4574 17.3749 17.929 17.553 18.32 17.9092C18.7111 18.2654 18.9066 18.695 18.9066 19.1979C18.9066 19.6868 18.7111 20.1094 18.32 20.4657Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

+2 -2
View File
@@ -1,3 +1,3 @@
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 18H4.242H4.243L17.678 4.565C18.0684 4.1745 18.0684 3.5415 17.678 3.151L14.849 0.322C14.4585 -0.0683821 13.8255 -0.0683821 13.435 0.322L0 13.757V18ZM12.728 3.858L14.142 5.272L15.556 3.858L14.142 2.444L12.728 3.858ZM12.728 6.686L11.314 5.272L2 14.586V16H3.414L12.728 6.686Z" fill="black"/>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3 21H7.242H7.243L20.678 7.565C21.0684 7.1745 21.0684 6.5415 20.678 6.151L17.849 3.322C17.4585 2.93162 16.8255 2.93162 16.435 3.322L3 16.757V21ZM15.728 6.858L17.142 8.272L18.556 6.858L17.142 5.444L15.728 6.858ZM15.728 9.686L14.314 8.272L5 17.586V19H6.414L15.728 9.686Z" fill="black"/>
</svg>

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 437 B

-209
View File
@@ -1,209 +0,0 @@
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart';
import 'package:stream_chat_flutter/src/stream_channel.dart';
import '../stream_chat_flutter.dart';
import 'message_input.dart';
import 'stream_chat.dart';
class MessageActionsBottomSheet extends StatelessWidget {
final Widget Function(BuildContext, Message) editMessageInputBuilder;
final void Function(Message) onThreadTap;
final Message message;
final bool showReactions;
final bool showDeleteMessage;
final bool showEditMessage;
final bool showReply;
final Map<String, String> reactionToEmoji = const {
'love': '❤️',
'haha': '😂',
'like': '👍',
'sad': '😕',
'angry': '😡',
'wow': '😲',
};
const MessageActionsBottomSheet({
Key key,
this.message,
this.showReactions,
this.showDeleteMessage,
this.showEditMessage,
this.onThreadTap,
this.showReply,
this.editMessageInputBuilder,
}) : super(key: key);
@override
Widget build(BuildContext context) {
final channel = StreamChannel.of(context).channel;
return SafeArea(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
if (showReactions &&
(message.status == MessageSendingStatus.SENT ||
message.status == null))
ReactionPicker(
channel: channel,
reactionToEmoji: reactionToEmoji,
message: message,
),
if (showDeleteMessage) _buildDeleteButton(context),
if (showEditMessage) _buildEditMessage(context),
if (showReply &&
(message.status == MessageSendingStatus.SENT ||
message.status == null) &&
message.parentId == null)
_buildReplyButton(context),
],
),
);
}
FlatButton _buildDeleteButton(BuildContext context) {
return FlatButton(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Delete message',
style:
Theme.of(context).textTheme.headline5.copyWith(color: Colors.red),
),
),
onPressed: () {
Navigator.pop(context);
StreamChat.of(context).client.deleteMessage(
message,
StreamChannel.of(context).channel.cid,
);
},
);
}
FlatButton _buildEditMessage(BuildContext context) {
return FlatButton(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Edit message',
style: Theme.of(context).textTheme.headline5,
),
),
onPressed: () async {
Navigator.pop(context);
_showEditBottomSheet(context);
},
);
}
void _showEditBottomSheet(BuildContext context) {
final channel = StreamChannel.of(context).channel;
showModalBottomSheet(
context: context,
elevation: 2,
clipBehavior: Clip.hardEdge,
isScrollControlled: true,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topLeft: Radius.circular(32),
topRight: Radius.circular(32),
),
),
builder: (context) {
return StreamChannel(
channel: channel,
child: Flex(
direction: Axis.vertical,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(
top: 16.0,
left: 16.0,
right: 16.0,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Edit message',
style: Theme.of(context).textTheme.headline6,
),
Container(
height: 30,
padding: const EdgeInsets.all(2.0),
child: AspectRatio(
aspectRatio: 1,
child: RawMaterialButton(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(4),
),
elevation: 0,
highlightElevation: 0,
focusElevation: 0,
disabledElevation: 0,
hoverElevation: 0,
onPressed: () {
Navigator.of(context).pop();
},
fillColor:
Theme.of(context).brightness == Brightness.dark
? Colors.white.withOpacity(.1)
: Colors.black.withOpacity(.1),
padding: EdgeInsets.all(4),
child: Icon(
Icons.close,
size: 15,
color: StreamChatTheme.of(context)
.primaryIconTheme
.color,
),
),
),
),
],
),
),
Padding(
padding: EdgeInsets.only(
bottom: MediaQuery.of(context).viewInsets.bottom,
),
child: editMessageInputBuilder != null
? editMessageInputBuilder(context, message)
: MessageInput(
editMessage: message,
onMessageSent: (_) {
FocusScope.of(context).unfocus();
Navigator.pop(context);
},
),
),
],
),
);
},
);
}
FlatButton _buildReplyButton(BuildContext context) {
return FlatButton(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
'Start a thread',
style: Theme.of(context).textTheme.headline5,
),
),
onPressed: () {
Navigator.pop(context);
if (onThreadTap != null) {
onThreadTap(message);
}
},
);
}
}
+9 -14
View File
@@ -6,9 +6,10 @@ import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/src/reaction_picker.dart';
import 'package:stream_chat_flutter/src/stream_channel.dart';
import '../stream_chat_flutter.dart';
import 'message_input.dart';
import 'message_widget.dart';
import 'stream_chat.dart';
import 'stream_chat_theme.dart';
class MessageActionsModal extends StatelessWidget {
final Widget Function(BuildContext, Message) editMessageInputBuilder;
@@ -21,14 +22,6 @@ class MessageActionsModal extends StatelessWidget {
final bool showReply;
final bool reverse;
final ShapeBorder messageShape;
final Map<String, String> reactionToEmoji = const {
'love': '❤️',
'haha': '😂',
'like': '👍',
'sad': '😕',
'angry': '😡',
'wow': '😲',
};
const MessageActionsModal({
Key key,
@@ -73,10 +66,12 @@ class MessageActionsModal extends StatelessWidget {
if (showReactions &&
(message.status == MessageSendingStatus.SENT ||
message.status == null))
ReactionPicker(
channel: channel,
reactionToEmoji: reactionToEmoji,
message: message,
Center(
child: ReactionPicker(
channel: channel,
message: message,
messageTheme: messageTheme,
),
),
AbsorbPointer(
child: MessageWidget(
@@ -158,7 +153,7 @@ class MessageActionsModal extends StatelessWidget {
alignment: Alignment.center,
package: 'stream_chat_flutter',
color: StreamChatTheme.of(context).primaryIconTheme.color,
width: 17,
width: 24,
),
onTap: () async {
Navigator.pop(context);
+123 -35
View File
@@ -5,6 +5,7 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_portal/flutter_portal.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:jiffy/jiffy.dart';
import 'package:stream_chat_flutter/src/message_actions_modal.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
@@ -193,15 +194,6 @@ class MessageWidget extends StatefulWidget {
}
class _MessageWidgetState extends State<MessageWidget> {
final Map<String, String> _reactionToEmoji = {
'love': '❤️',
'haha': '😂',
'like': '👍',
'sad': '😕',
'angry': '😡',
'wow': '😲',
};
final GlobalKey _reactionPickerKey = GlobalKey();
double _reactionPadding = 0;
@@ -341,9 +333,9 @@ class _MessageWidgetState extends State<MessageWidget> {
offset: Offset(4, 0),
child: CustomPaint(
painter: ReactionBubblePainter(
Theme.of(context).brightness == Brightness.dark
? Colors.white
: Colors.black,
widget.messageTheme.reactionsBackgroundColor,
widget.messageTheme.reactionsBorderColor,
widget.message.reactionCounts.length,
),
),
)
@@ -456,14 +448,23 @@ class _MessageWidgetState extends State<MessageWidget> {
child: Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.white
: Colors.black,
border: widget.messageTheme
.reactionsBackgroundColor ==
StreamChatTheme.of(context)
.backgroundColor
? Border.all(
color: Theme.of(context).brightness ==
Brightness.dark
? Colors.white.withAlpha(24)
: Colors.black.withAlpha(24),
)
: null,
color:
widget.messageTheme.reactionsBackgroundColor,
borderRadius:
BorderRadius.all(Radius.circular(14)),
),
child: _buildReactionsText(context),
child: _buildReactions(context),
),
),
_buildReactionsTail(context),
@@ -477,18 +478,33 @@ class _MessageWidgetState extends State<MessageWidget> {
);
}
Text _buildReactionsText(BuildContext context) {
return Text(
widget.message.reactionCounts.keys.map((reactionType) {
return _reactionToEmoji[reactionType] ?? '?';
}).join(' ') +
' ${widget.message.reactionCounts.values.fold(0, (t, v) => v + t).toString()}',
style: TextStyle(
color: Theme.of(context).brightness == Brightness.dark
? Colors.black
: Colors.white,
),
textAlign: TextAlign.justify,
Widget _buildReactions(BuildContext context) {
final reactionAssets = StreamChatTheme.of(context).reactionAssets;
return Row(
mainAxisSize: MainAxisSize.min,
children: [
...widget.message.reactionCounts.keys.map((reactionType) {
final reactionAsset = reactionAssets.firstWhere(
(reactionAsset) => reactionAsset.type == reactionType,
orElse: () => null,
);
if (reactionAsset == null) {
return Text(
'?',
style: TextStyle(
color: StreamChatTheme.of(context).accentColor,
),
);
}
return SvgPicture.asset(
reactionAsset.svgAsset,
package: reactionAsset.package,
height: 16,
color: StreamChatTheme.of(context).accentColor,
);
}).toList(),
],
);
}
@@ -807,17 +823,89 @@ class _MessageWidgetState extends State<MessageWidget> {
class ReactionBubblePainter extends CustomPainter {
final Color color;
final Color borderColor;
final int reactionsCount;
ReactionBubblePainter(this.color);
ReactionBubblePainter(
this.color,
this.borderColor,
this.reactionsCount,
);
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()..color = color;
_drawArc(size, canvas);
_drawBorder(size, canvas);
_drawOval(size, canvas);
_drawOvalBorder(size, canvas);
}
void _drawOvalBorder(Size size, Canvas canvas) {
final paint = Paint()
..color = borderColor
..strokeWidth = 1
..style = PaintingStyle.stroke;
final path = Path();
path.lineTo(-2, -6);
path.lineTo(0, 10);
path.lineTo(10, -6);
path.lineTo(-2, -6);
path.addOval(
Rect.fromCircle(
center: Offset(0, 3),
radius: 2,
),
);
canvas.drawPath(path, paint);
}
void _drawOval(Size size, Canvas canvas) {
final paint = Paint()
..color = color
..strokeWidth = 1;
final path = Path();
path.addOval(Rect.fromCircle(
center: Offset(0, 3),
radius: 2,
));
canvas.drawPath(path, paint);
}
void _drawBorder(Size size, Canvas canvas) {
final paint = Paint()
..color = borderColor
..strokeWidth = 1
..style = PaintingStyle.stroke;
final dy = reactionsCount > 1 ? -2.0 : -3.0;
final path = Path();
path.addArc(
Rect.fromCircle(
center: Offset(-4, dy),
radius: 4,
),
-pi * 1.15,
-pi / 1.4,
);
canvas.drawPath(path, paint);
}
void _drawArc(Size size, Canvas canvas) {
final paint = Paint()
..color = color
..strokeWidth = 1;
final dy = reactionsCount > 1 ? -2.0 : -3.0;
final path = Path();
path.addArc(
Rect.fromCircle(
center: Offset(-4, dy),
radius: 4,
),
-pi,
-pi,
);
canvas.drawPath(path, paint);
}
+11
View File
@@ -0,0 +1,11 @@
class ReactionAsset {
final String type;
final String svgAsset;
final String package;
ReactionAsset({
this.type,
this.svgAsset,
this.package,
});
}
+34 -39
View File
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import '../stream_chat_flutter.dart';
@@ -11,60 +12,54 @@ import '../stream_chat_flutter.dart';
class ReactionPicker extends StatelessWidget {
const ReactionPicker({
Key key,
@required this.reactionToEmoji,
@required this.message,
@required this.channel,
this.size = 30,
@required this.messageTheme,
}) : super(key: key);
final Map<String, String> reactionToEmoji;
final Message message;
final double size;
final MessageTheme messageTheme;
final Channel channel;
@override
Widget build(BuildContext context) {
final reactionAssets = StreamChatTheme.of(context).reactionAssets;
return Material(
color: Colors.black87,
color: messageTheme.reactionsBackgroundColor,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: reactionToEmoji.keys.map((reactionType) {
final ownReactionIndex = message.ownReactions
?.indexWhere((reaction) => reaction.type == reactionType) ??
children: reactionAssets.map((reactionAsset) {
final ownReactionIndex = message.ownReactions?.indexWhere(
(reaction) => reaction.type == reactionAsset.type) ??
-1;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
IconButton(
iconSize: size,
icon: Text(
reactionToEmoji[reactionType],
style: TextStyle(
fontSize: size - 10,
),
),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(
context, message.ownReactions[ownReactionIndex]);
} else {
sendReaction(context, reactionType);
}
},
),
ownReactionIndex != -1
? Padding(
padding: const EdgeInsets.only(bottom: 4.0),
child: Text(
message.ownReactions[ownReactionIndex].score.toString(),
style: TextStyle(color: Colors.white),
),
)
: SizedBox(),
],
return IconButton(
iconSize: 24,
icon: SvgPicture.asset(
reactionAsset.svgAsset,
package: reactionAsset.package,
color: ownReactionIndex != -1
? StreamChatTheme.of(context).accentColor
: Colors.black,
),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(
context,
message.ownReactions[ownReactionIndex],
);
} else {
sendReaction(
context,
reactionAsset.type,
);
}
},
);
}).toList(),
),
+50 -1
View File
@@ -4,6 +4,8 @@ import 'package:stream_chat_flutter/src/channel_header.dart';
import 'package:stream_chat_flutter/src/channel_preview.dart';
import 'package:stream_chat_flutter/src/message_input.dart';
import 'reaction_asset.dart';
/// Inherited widget providing the [StreamChatThemeData] to the widget tree
class StreamChatTheme extends InheritedWidget {
final StreamChatThemeData data;
@@ -72,6 +74,9 @@ class StreamChatThemeData {
/// Primary icon theme
final IconThemeData primaryIconTheme;
/// Assets used for rendering reactions
final List<ReactionAsset> reactionAssets;
/// Create a theme from scratch
StreamChatThemeData({
this.primaryColor,
@@ -85,6 +90,7 @@ class StreamChatThemeData {
this.defaultChannelImage,
this.defaultUserImage,
this.primaryIconTheme,
this.reactionAssets,
});
/// Create a theme from a Material [Theme]
@@ -135,6 +141,7 @@ class StreamChatThemeData {
Widget Function(BuildContext, Channel) defaultChannelImage,
Widget Function(BuildContext, User) defaultUserImage,
IconThemeData primaryIconTheme,
List<ReactionAsset> reactionAssets,
}) =>
StreamChatThemeData(
primaryColor: primaryColor ?? this.primaryColor,
@@ -203,6 +210,7 @@ class StreamChatThemeData {
this.otherMessageTheme.avatarTheme,
) ??
this.otherMessageTheme,
reactionAssets: reactionAssets ?? this.reactionAssets,
);
/// Get the default Stream Chat theme
@@ -293,7 +301,9 @@ class StreamChatThemeData {
fontWeight: FontWeight.bold,
fontSize: 12,
),
messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffE6E6E6),
messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
reactionsBackgroundColor: isDark ? Colors.black : Colors.white,
reactionsBorderColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor(
@@ -306,6 +316,9 @@ class StreamChatThemeData {
),
),
otherMessageTheme: MessageTheme(
reactionsBackgroundColor:
isDark ? Color(0xff191919) : Color(0xffEAEAEA),
reactionsBorderColor: theme.scaffoldBackgroundColor,
messageText: TextStyle(
fontSize: 15,
color: isDark ? Colors.white : Colors.black,
@@ -333,6 +346,33 @@ class StreamChatThemeData {
),
),
),
reactionAssets: [
ReactionAsset(
type: 'love',
svgAsset: 'assets/Icon_love_reaction.svg',
package: 'stream_chat_flutter',
),
ReactionAsset(
type: 'thumbs_up',
svgAsset: 'assets/Icon_thumbs_up_reaction.svg',
package: 'stream_chat_flutter',
),
ReactionAsset(
type: 'thumbs_down',
svgAsset: 'assets/Icon_thumbs_down_reaction.svg',
package: 'stream_chat_flutter',
),
ReactionAsset(
type: 'lol',
svgAsset: 'assets/Icon_LOL_reaction.svg',
package: 'stream_chat_flutter',
),
ReactionAsset(
type: 'wut',
svgAsset: 'assets/Icon_wut_reaction.svg',
package: 'stream_chat_flutter',
),
],
);
}
}
@@ -415,6 +455,8 @@ class MessageTheme {
final TextStyle createdAt;
final TextStyle replies;
final Color messageBackgroundColor;
final Color reactionsBackgroundColor;
final Color reactionsBorderColor;
final AvatarTheme avatarTheme;
const MessageTheme({
@@ -423,6 +465,8 @@ class MessageTheme {
this.messageAuthor,
this.messageLinks,
this.messageBackgroundColor,
this.reactionsBackgroundColor,
this.reactionsBorderColor,
this.avatarTheme,
this.createdAt,
});
@@ -436,6 +480,8 @@ class MessageTheme {
Color messageBackgroundColor,
Color otherMessageBackgroundColor,
AvatarTheme avatarTheme,
Color reactionsBackgroundColor,
Color reactionsBorderColor,
}) =>
MessageTheme(
messageText: messageText ?? this.messageText,
@@ -446,6 +492,9 @@ class MessageTheme {
messageBackgroundColor ?? this.messageBackgroundColor,
avatarTheme: avatarTheme ?? this.avatarTheme,
replies: replies ?? this.replies,
reactionsBackgroundColor:
reactionsBackgroundColor ?? this.reactionsBackgroundColor,
reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor,
);
}
+1 -1
View File
@@ -24,7 +24,7 @@ dependencies:
image_picker: ^0.6.7+2
flutter_keyboard_visibility: ^3.2.1
flutter_svg: ^0.18.0
stream_chat: ^0.2.3
stream_chat: ^0.2.3+3
mime: ^0.9.6+3
visibility_detector: ^0.1.5
line_awesome_icons: ^1.0.4+2