Merge pull request #474 from GetStream/feat/customReaction
feat: let users use custom reaction builders
This commit is contained in:
@@ -5,6 +5,7 @@
|
|||||||
- Performance improvements
|
- Performance improvements
|
||||||
- Added pinMessage ui support
|
- Added pinMessage ui support
|
||||||
- Added `MessageListView.threadSeparatorBuilder` property
|
- Added `MessageListView.threadSeparatorBuilder` property
|
||||||
|
|
||||||
## 2.0.0-nullsafety.4
|
## 2.0.0-nullsafety.4
|
||||||
|
|
||||||
- Minor fixes and improvements
|
- Minor fixes and improvements
|
||||||
|
|||||||
@@ -148,6 +148,10 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
|
|
||||||
final streamChatThemeData = StreamChatTheme.of(context);
|
final streamChatThemeData = StreamChatTheme.of(context);
|
||||||
|
|
||||||
|
final numberOfReactions = streamChatThemeData.reactionIcons.length;
|
||||||
|
final shiftFactor =
|
||||||
|
numberOfReactions < 5 ? (5 - numberOfReactions) * 0.1 : 0.0;
|
||||||
|
|
||||||
final child = Center(
|
final child = Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -162,8 +166,12 @@ class _MessageActionsModalState extends State<MessageActionsModal> {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment(
|
alignment: Alignment(
|
||||||
user?.id == widget.message.user?.id
|
user?.id == widget.message.user?.id
|
||||||
? (divFactor >= 1.0 ? -0.2 : (1.2 - divFactor))
|
? (divFactor >= 1.0
|
||||||
: (divFactor >= 1.0 ? 0.2 : -(1.2 - divFactor)),
|
? -0.2 - shiftFactor
|
||||||
|
: (1.2 - divFactor))
|
||||||
|
: (divFactor >= 1.0
|
||||||
|
? 0.2 + shiftFactor
|
||||||
|
: -(1.2 - divFactor)),
|
||||||
0),
|
0),
|
||||||
child: ReactionPicker(
|
child: ReactionPicker(
|
||||||
message: widget.message,
|
message: widget.message,
|
||||||
|
|||||||
@@ -80,6 +80,10 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
final hasFileAttachment =
|
final hasFileAttachment =
|
||||||
message.attachments.any((it) => it.type == 'file') == true;
|
message.attachments.any((it) => it.type == 'file') == true;
|
||||||
|
|
||||||
|
final numberOfReactions = StreamChatTheme.of(context).reactionIcons.length;
|
||||||
|
final shiftFactor =
|
||||||
|
numberOfReactions < 5 ? (5 - numberOfReactions) * 0.1 : 0.0;
|
||||||
|
|
||||||
final child = Center(
|
final child = Center(
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
@@ -93,8 +97,12 @@ class MessageReactionsModal extends StatelessWidget {
|
|||||||
Align(
|
Align(
|
||||||
alignment: Alignment(
|
alignment: Alignment(
|
||||||
user!.id == message.user!.id
|
user!.id == message.user!.id
|
||||||
? (divFactor >= 1.0 ? -0.2 : (1.2 - divFactor))
|
? (divFactor >= 1.0
|
||||||
: (divFactor >= 1.0 ? 0.2 : -(1.2 - divFactor)),
|
? -0.2 - shiftFactor
|
||||||
|
: (1.2 - divFactor))
|
||||||
|
: (divFactor >= 1.0
|
||||||
|
? 0.2 + shiftFactor
|
||||||
|
: -(1.2 - divFactor)),
|
||||||
0),
|
0),
|
||||||
child: ReactionPicker(
|
child: ReactionPicker(
|
||||||
message: message,
|
message: message,
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:stream_chat_flutter/src/reaction_icon.dart';
|
import 'package:stream_chat_flutter/src/reaction_icon.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// Creates reaction bubble widget for displaying over messages
|
/// Creates reaction bubble widget for displaying over messages
|
||||||
@@ -130,13 +129,13 @@ class ReactionBubble extends StatelessWidget {
|
|||||||
horizontal: 4,
|
horizontal: 4,
|
||||||
),
|
),
|
||||||
child: reactionIcon != null
|
child: reactionIcon != null
|
||||||
? StreamSvgIcon(
|
? ConstrainedBox(
|
||||||
assetName: reactionIcon.assetName,
|
constraints: BoxConstraints.tight(const Size.square(16)),
|
||||||
width: 16,
|
child: reactionIcon.builder(
|
||||||
height: 16,
|
context,
|
||||||
color: (!highlightOwnReactions || reaction.user?.id == userId)
|
!highlightOwnReactions || reaction.user?.id == userId,
|
||||||
? chatThemeData.colorTheme.accentBlue
|
16,
|
||||||
: chatThemeData.colorTheme.black.withOpacity(.5),
|
),
|
||||||
)
|
)
|
||||||
: Icon(
|
: Icon(
|
||||||
Icons.help_outline_rounded,
|
Icons.help_outline_rounded,
|
||||||
|
|||||||
@@ -1,14 +1,20 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
/// Reaction icon data
|
/// Reaction icon data
|
||||||
class ReactionIcon {
|
class ReactionIcon {
|
||||||
/// Constructor for creating [ReactionIcon]
|
/// Constructor for creating [ReactionIcon]
|
||||||
ReactionIcon({
|
ReactionIcon({
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.assetName,
|
required this.builder,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Type of reaction
|
/// Type of reaction
|
||||||
final String type;
|
final String type;
|
||||||
|
|
||||||
/// Asset to display for reaction
|
/// Asset to display for reaction
|
||||||
final String assetName;
|
final Widget Function(
|
||||||
|
BuildContext,
|
||||||
|
bool highlighted,
|
||||||
|
double size,
|
||||||
|
) builder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import 'package:ezanimation/ezanimation.dart';
|
import 'package:ezanimation/ezanimation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:stream_chat_flutter/src/extension.dart';
|
import 'package:stream_chat_flutter/src/extension.dart';
|
||||||
import 'package:stream_chat_flutter/src/stream_svg_icon.dart';
|
|
||||||
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
|
||||||
|
|
||||||
/// 
|
/// 
|
||||||
@@ -69,11 +68,10 @@ class _ReactionPickerState extends State<ReactionPicker>
|
|||||||
-1;
|
-1;
|
||||||
final index = reactionIcons.indexOf(reactionIcon);
|
final index = reactionIcons.indexOf(reactionIcon);
|
||||||
|
|
||||||
final child = StreamSvgIcon(
|
final child = reactionIcon.builder(
|
||||||
assetName: reactionIcon.assetName,
|
context,
|
||||||
color: ownReactionIndex != -1
|
ownReactionIndex != -1,
|
||||||
? chatThemeData.colorTheme.accentBlue
|
24,
|
||||||
: Theme.of(context).iconTheme.color!.withOpacity(.5),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return ConstrainedBox(
|
return ConstrainedBox(
|
||||||
|
|||||||
@@ -217,10 +217,11 @@ class StreamChatThemeData {
|
|||||||
TextTheme textTheme,
|
TextTheme textTheme,
|
||||||
) {
|
) {
|
||||||
final accentColor = colorTheme.accentBlue;
|
final accentColor = colorTheme.accentBlue;
|
||||||
|
final iconTheme = IconThemeData(color: colorTheme.black.withOpacity(.5));
|
||||||
return StreamChatThemeData.raw(
|
return StreamChatThemeData.raw(
|
||||||
textTheme: textTheme,
|
textTheme: textTheme,
|
||||||
colorTheme: colorTheme,
|
colorTheme: colorTheme,
|
||||||
primaryIconTheme: IconThemeData(color: colorTheme.black.withOpacity(.5)),
|
primaryIconTheme: iconTheme,
|
||||||
defaultChannelImage: (context, channel) => const SizedBox(),
|
defaultChannelImage: (context, channel) => const SizedBox(),
|
||||||
defaultUserImage: (context, user) => Center(
|
defaultUserImage: (context, user) => Center(
|
||||||
child: CachedNetworkImage(
|
child: CachedNetworkImage(
|
||||||
@@ -342,23 +343,63 @@ class StreamChatThemeData {
|
|||||||
reactionIcons: [
|
reactionIcons: [
|
||||||
ReactionIcon(
|
ReactionIcon(
|
||||||
type: 'love',
|
type: 'love',
|
||||||
assetName: 'Icon_love_reaction.svg',
|
builder: (context, highlighted, size) {
|
||||||
|
final theme = StreamChatTheme.of(context);
|
||||||
|
return StreamSvgIcon.loveReaction(
|
||||||
|
color: highlighted
|
||||||
|
? theme.colorTheme.accentBlue
|
||||||
|
: theme.primaryIconTheme.color!.withOpacity(.5),
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ReactionIcon(
|
ReactionIcon(
|
||||||
type: 'like',
|
type: 'like',
|
||||||
assetName: 'Icon_thumbs_up_reaction.svg',
|
builder: (context, highlighted, size) {
|
||||||
|
final theme = StreamChatTheme.of(context);
|
||||||
|
return StreamSvgIcon.thumbsUpReaction(
|
||||||
|
color: highlighted
|
||||||
|
? theme.colorTheme.accentBlue
|
||||||
|
: theme.primaryIconTheme.color!.withOpacity(.5),
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ReactionIcon(
|
ReactionIcon(
|
||||||
type: 'sad',
|
type: 'sad',
|
||||||
assetName: 'Icon_thumbs_down_reaction.svg',
|
builder: (context, highlighted, size) {
|
||||||
|
final theme = StreamChatTheme.of(context);
|
||||||
|
return StreamSvgIcon.thumbsDownReaction(
|
||||||
|
color: highlighted
|
||||||
|
? theme.colorTheme.accentBlue
|
||||||
|
: theme.primaryIconTheme.color!.withOpacity(.5),
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ReactionIcon(
|
ReactionIcon(
|
||||||
type: 'haha',
|
type: 'haha',
|
||||||
assetName: 'Icon_LOL_reaction.svg',
|
builder: (context, highlighted, size) {
|
||||||
|
final theme = StreamChatTheme.of(context);
|
||||||
|
return StreamSvgIcon.lolReaction(
|
||||||
|
color: highlighted
|
||||||
|
? theme.colorTheme.accentBlue
|
||||||
|
: theme.primaryIconTheme.color!.withOpacity(.5),
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
ReactionIcon(
|
ReactionIcon(
|
||||||
type: 'wow',
|
type: 'wow',
|
||||||
assetName: 'Icon_wut_reaction.svg',
|
builder: (context, highlighted, size) {
|
||||||
|
final theme = StreamChatTheme.of(context);
|
||||||
|
return StreamSvgIcon.wutReaction(
|
||||||
|
color: highlighted
|
||||||
|
? theme.colorTheme.accentBlue
|
||||||
|
: theme.primaryIconTheme.color!.withOpacity(.5),
|
||||||
|
size: size,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -49,6 +49,66 @@ class StreamSvgIcon extends StatelessWidget {
|
|||||||
height: size,
|
height: size,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/// [StreamSvgIcon] type
|
||||||
|
factory StreamSvgIcon.loveReaction({
|
||||||
|
double? size,
|
||||||
|
Color? color,
|
||||||
|
}) =>
|
||||||
|
StreamSvgIcon(
|
||||||
|
assetName: 'Icon_love_reaction.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// [StreamSvgIcon] type
|
||||||
|
factory StreamSvgIcon.thumbsUpReaction({
|
||||||
|
double? size,
|
||||||
|
Color? color,
|
||||||
|
}) =>
|
||||||
|
StreamSvgIcon(
|
||||||
|
assetName: 'Icon_thumbs_up_reaction.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// [StreamSvgIcon] type
|
||||||
|
factory StreamSvgIcon.thumbsDownReaction({
|
||||||
|
double? size,
|
||||||
|
Color? color,
|
||||||
|
}) =>
|
||||||
|
StreamSvgIcon(
|
||||||
|
assetName: 'Icon_thumbs_down_reaction.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// [StreamSvgIcon] type
|
||||||
|
factory StreamSvgIcon.lolReaction({
|
||||||
|
double? size,
|
||||||
|
Color? color,
|
||||||
|
}) =>
|
||||||
|
StreamSvgIcon(
|
||||||
|
assetName: 'Icon_LOL_reaction.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// [StreamSvgIcon] type
|
||||||
|
factory StreamSvgIcon.wutReaction({
|
||||||
|
double? size,
|
||||||
|
Color? color,
|
||||||
|
}) =>
|
||||||
|
StreamSvgIcon(
|
||||||
|
assetName: 'Icon_wut_reaction.svg',
|
||||||
|
color: color,
|
||||||
|
width: size,
|
||||||
|
height: size,
|
||||||
|
);
|
||||||
|
|
||||||
/// [StreamSvgIcon] type
|
/// [StreamSvgIcon] type
|
||||||
factory StreamSvgIcon.smile({
|
factory StreamSvgIcon.smile({
|
||||||
double? size,
|
double? size,
|
||||||
|
|||||||
Reference in New Issue
Block a user