Initial implementation

Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
Sahil Kumar
2020-12-22 21:45:14 +05:30
parent aa5433a881
commit 209c5a3c24
10 changed files with 359 additions and 313 deletions
+1
View File
@@ -470,6 +470,7 @@ class ThreadPage extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
backgroundColor: Color.fromRGBO(252, 252, 252, 1),
appBar: ThreadHeader( appBar: ThreadHeader(
parent: parent, parent: parent,
), ),
+1 -1
View File
@@ -113,7 +113,7 @@ class MessageActionsModal extends StatelessWidget {
messageTheme: messageTheme, messageTheme: messageTheme,
showReactions: false, showReactions: false,
showUsername: false, showUsername: false,
showReplyIndicator: false, showThreadReplyIndicator: false,
showUserAvatar: showUserAvatar, showUserAvatar: showUserAvatar,
showTimestamp: false, showTimestamp: false,
translateUserAvatar: false, translateUserAvatar: false,
+22 -12
View File
@@ -241,7 +241,7 @@ class _MessageListViewState extends State<MessageListView> {
builder: (context, snapshot) { builder: (context, snapshot) {
if (!snapshot.hasData) { if (!snapshot.hasData) {
return Center( return Center(
child: CircularProgressIndicator(), child: const CircularProgressIndicator(),
); );
} }
@@ -336,18 +336,25 @@ class _MessageListViewState extends State<MessageListView> {
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: <Widget>[
buildParentMessage(widget.parentMessage), buildParentMessage(widget.parentMessage),
Padding( Container(
padding: decoration: BoxDecoration(
const EdgeInsets.symmetric(horizontal: 32), gradient: LinearGradient(
child: Container( colors: [
padding: const EdgeInsets.all(8), Color(0XFFF7F7F7),
Color(0XFFFCFCFC),
],
),
),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Text( child: Text(
'Start of thread', '${widget.parentMessage.replyCount} ${widget.parentMessage.replyCount == 1 ? 'Reply' : 'Replies'}',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: StreamChatTheme.of(context)
.channelTheme
.channelHeaderTheme
.lastMessageAt,
), ),
color: Theme.of(context)
.accentColor
.withAlpha(50),
), ),
), ),
], ],
@@ -585,7 +592,7 @@ class _MessageListViewState extends State<MessageListView> {
if (!snapshot.data) { if (!snapshot.data) {
if (direction == QueryDirection.top) { if (direction == QueryDirection.top) {
return Container( return Container(
height: 50, height: 52,
width: double.infinity, width: double.infinity,
); );
} }
@@ -679,7 +686,8 @@ class _MessageListViewState extends State<MessageListView> {
final isMyMessage = message.user.id == StreamChat.of(context).user.id; final isMyMessage = message.user.id == StreamChat.of(context).user.id;
return MessageWidget( return MessageWidget(
showReplyIndicator: false, showThreadReplyIndicator: false,
showInChannelIndicator: false,
message: message, message: message,
reverse: isMyMessage, reverse: isMyMessage,
showUsername: !isMyMessage, showUsername: !isMyMessage,
@@ -748,6 +756,8 @@ class _MessageListViewState extends State<MessageListView> {
bottom: index == 0 ? 30 : (isNextUser ? 2 : 7), bottom: index == 0 ? 30 : (isNextUser ? 2 : 7),
top: 3, top: 3,
), ),
showInChannelIndicator: widget.parentMessage == null,
showThreadReplyIndicator: widget.parentMessage == null,
showUsername: !isMyMessage && !isNextUser, showUsername: !isMyMessage && !isNextUser,
showSendingIndicator: isMyMessage && showSendingIndicator: isMyMessage &&
(index == 0 || message.status != MessageSendingStatus.SENT) (index == 0 || message.status != MessageSendingStatus.SENT)
+1 -1
View File
@@ -105,7 +105,7 @@ class MessageReactionsModal extends StatelessWidget {
showReactions: false, showReactions: false,
showUsername: false, showUsername: false,
showUserAvatar: showUserAvatar, showUserAvatar: showUserAvatar,
showReplyIndicator: false, showThreadReplyIndicator: false,
showTimestamp: false, showTimestamp: false,
translateUserAvatar: false, translateUserAvatar: false,
showSendingIndicator: DisplayWidget.gone, showSendingIndicator: DisplayWidget.gone,
+297 -238
View File
@@ -99,8 +99,11 @@ class MessageWidget extends StatefulWidget {
final bool allRead; final bool allRead;
/// If true the widget will show the reply indicator /// If true the widget will show the thread reply indicator
final bool showReplyIndicator; final bool showThreadReplyIndicator;
/// If true the widget will show the show in channel indicator
final bool showInChannelIndicator;
/// The function called when tapping on UserAvatar /// The function called when tapping on UserAvatar
final void Function(User) onUserAvatarTap; final void Function(User) onUserAvatarTap;
@@ -123,6 +126,7 @@ class MessageWidget extends StatefulWidget {
/// Center user avatar with bottom of the message /// Center user avatar with bottom of the message
final bool translateUserAvatar; final bool translateUserAvatar;
///
MessageWidget({ MessageWidget({
Key key, Key key,
@required this.message, @required this.message,
@@ -139,7 +143,8 @@ class MessageWidget extends StatefulWidget {
this.showReactionPickerIndicator = false, this.showReactionPickerIndicator = false,
this.showUserAvatar = DisplayWidget.show, this.showUserAvatar = DisplayWidget.show,
this.showSendingIndicator = DisplayWidget.show, this.showSendingIndicator = DisplayWidget.show,
this.showReplyIndicator = true, this.showThreadReplyIndicator = true,
this.showInChannelIndicator = true,
this.onThreadTap, this.onThreadTap,
this.showUsername = true, this.showUsername = true,
this.showTimestamp = true, this.showTimestamp = true,
@@ -211,10 +216,23 @@ class MessageWidget extends StatefulWidget {
} }
class _MessageWidgetState extends State<MessageWidget> { class _MessageWidgetState extends State<MessageWidget> {
bool get showThreadReplyIndicator =>
widget.showThreadReplyIndicator && widget.message.replyCount > 0;
bool get showUsername => widget.showUsername;
bool get showTimeStamp =>
widget.message.createdAt != null && widget.showTimestamp;
bool get showReadList => widget.readList?.isNotEmpty == true;
bool get showInChannel =>
widget.showInChannelIndicator && widget.message?.showInChannel == true;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
var leftPadding = widget.showUserAvatar != DisplayWidget.gone var leftPadding = widget.showUserAvatar != DisplayWidget.gone
? widget.messageTheme.avatarTheme.constraints.maxWidth + 16.0 ? widget.messageTheme.avatarTheme.constraints.maxWidth + 14.5
: 6.0; : 6.0;
final hasFiles = final hasFiles =
@@ -234,140 +252,151 @@ class _MessageWidgetState extends State<MessageWidget> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: <Widget>[
Column( Stack(
crossAxisAlignment: CrossAxisAlignment.start, alignment: AlignmentDirectional.bottomStart,
mainAxisSize: MainAxisSize.min, children: [
children: <Widget>[ Column(
Row( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: <Widget>[ children: [
if (widget.showUserAvatar == DisplayWidget.show) Row(
_buildUserAvatar(), mainAxisAlignment: MainAxisAlignment.start,
SizedBox( crossAxisAlignment: CrossAxisAlignment.end,
width: 6, mainAxisSize: MainAxisSize.min,
), children: <Widget>[
if (widget.showUserAvatar == DisplayWidget.hide) if (widget.showUserAvatar == DisplayWidget.show)
SizedBox( _buildUserAvatar(),
width: widget.messageTheme.avatarTheme.constraints SizedBox(width: 6),
.maxWidth + if (widget.showUserAvatar == DisplayWidget.hide)
8, SizedBox(
), width: widget.messageTheme.avatarTheme
Flexible( .constraints.maxWidth +
child: PortalEntry( 8,
portal: Container( ),
transform: Matrix4.translationValues(-16, 2, 0), Flexible(
child: _buildReactionIndicator(context), child: PortalEntry(
constraints: BoxConstraints(maxWidth: 22 * 6.0), portal: Container(
), transform:
portalAnchor: Alignment(-1.0, -1.0), Matrix4.translationValues(-16, 2, 0),
childAnchor: Alignment(1, -1.0), child: _buildReactionIndicator(context),
child: Stack( constraints:
clipBehavior: Clip.none, BoxConstraints(maxWidth: 22 * 6.0),
children: [ ),
Padding( portalAnchor: Alignment(-1.0, -1.0),
padding: widget.showReactions childAnchor: Alignment(1, -1.0),
? EdgeInsets.only( child: Stack(
top: widget.message.reactionCounts clipBehavior: Clip.none,
?.isNotEmpty == children: [
true Padding(
? 18 padding: widget.showReactions
: 0, ? EdgeInsets.only(
) top: widget.message.reactionCounts
: EdgeInsets.zero, ?.isNotEmpty ==
child: (widget.message.isDeleted && true
widget.message.status != ? 18
MessageSendingStatus : 0,
.FAILED_DELETE) )
? Transform( : EdgeInsets.zero,
alignment: Alignment.center, child: (widget.message.isDeleted &&
widget.message.status !=
MessageSendingStatus
.FAILED_DELETE)
? Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(
widget.reverse ? pi : 0),
child: DeletedMessage(
reverse: widget.reverse,
borderRadiusGeometry:
widget.borderRadiusGeometry,
borderSide: widget.borderSide,
shape: widget.shape,
messageTheme:
widget.messageTheme,
),
)
: Material(
clipBehavior: Clip.antiAlias,
shape: widget.shape ??
RoundedRectangleBorder(
side: isOnlyEmoji
? BorderSide.none
: widget.borderSide ??
BorderSide(
color: Theme.of(context)
.brightness ==
Brightness
.dark
? Colors.white
.withAlpha(
24)
: Colors.black
.withAlpha(
24),
),
borderRadius: widget
.borderRadiusGeometry ??
BorderRadius.zero,
),
color: _getBackgroundColor(),
child: Padding(
padding: EdgeInsets.all(
hasFiles ? 2.0 : 0.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize:
MainAxisSize.min,
children: <Widget>[
..._parseAttachments(
context),
if (widget.message.text
.trim()
.isNotEmpty &&
!isGiphy)
_buildTextBubble(context),
],
),
),
),
),
if (widget.showReactionPickerIndicator)
Positioned(
right: 0,
top: -6,
child: Transform(
transform: Matrix4.rotationY( transform: Matrix4.rotationY(
widget.reverse ? pi : 0), widget.reverse ? pi : 0),
child: DeletedMessage( child: CustomPaint(
reverse: widget.reverse, painter: ReactionBubblePainter(
borderRadiusGeometry: widget.messageTheme
widget.borderRadiusGeometry, .reactionsBackgroundColor,
borderSide: widget.borderSide, widget.messageTheme
shape: widget.shape, .reactionsBorderColor,
messageTheme: widget.messageTheme,
),
)
: Material(
clipBehavior: Clip.antiAlias,
shape: widget.shape ??
RoundedRectangleBorder(
side: isOnlyEmoji
? BorderSide.none
: widget.borderSide ??
BorderSide(
color: Theme.of(context)
.brightness ==
Brightness
.dark
? Colors.white
.withAlpha(24)
: Colors.black
.withAlpha(
24),
),
borderRadius: widget
.borderRadiusGeometry ??
BorderRadius.zero,
),
color: _getBackgroundColor(),
child: Padding(
padding: EdgeInsets.all(
hasFiles ? 2.0 : 0.0),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
..._parseAttachments(context),
if (widget.message.text
.trim()
.isNotEmpty &&
!isGiphy)
_buildTextBubble(context),
],
), ),
), ),
), ),
),
if (widget.showReactionPickerIndicator)
Positioned(
right: 0,
top: -6,
child: Transform(
transform: Matrix4.rotationY(
widget.reverse ? pi : 0),
child: CustomPaint(
painter: ReactionBubblePainter(
widget.messageTheme
.reactionsBackgroundColor,
widget.messageTheme
.reactionsBorderColor,
),
), ),
), ],
), ),
], ),
), ),
), ],
), ),
if (showThreadReplyIndicator ||
showUsername ||
showTimeStamp ||
showInChannel)
SizedBox(height: 20.0),
], ],
), ),
if (widget.showReplyIndicator && if (showThreadReplyIndicator ||
widget.message.replyCount > 0) showUsername ||
_buildReplyIndicator(leftPadding), showTimeStamp ||
showInChannel)
_buildBottomRows(leftPadding)
], ],
), ),
if ((widget.message.createdAt != null &&
widget.showTimestamp) ||
widget.showUsername ||
widget.readList?.isNotEmpty == true)
_buildBottomRow(leftPadding),
], ],
), ),
), ),
@@ -376,6 +405,113 @@ class _MessageWidgetState extends State<MessageWidget> {
); );
} }
Widget _buildBottomRows(double leftPadding) {
final deleted = widget.message.isDeleted;
var children = <Widget>[];
if (deleted) {
children.add(
Row(
mainAxisSize: MainAxisSize.min,
children: [
StreamSvgIcon.eye(
color: Colors.black.withOpacity(0.5),
size: 16.0,
),
SizedBox(width: 8.0),
Text(
'Only visible to you',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 12.0,
),
),
],
),
);
} else {
final showSendingIndicator =
widget.showSendingIndicator == DisplayWidget.show;
final replyCount = widget.message.replyCount;
final msg = showInChannel
? 'Thread Reply'
: replyCount != 0
? '$replyCount ${replyCount > 1 ? 'Thread Replies' : 'Thread Reply'}'
: 'Thread Reply';
final onThreadTap = () async {
try {
var message = widget.message;
if (showInChannel && message.parentId != null) {
final channel = StreamChannel.of(context);
message = await channel.getMessage(widget.message.parentId);
}
return widget.onThreadTap(message);
} catch (e, stk) {
print(e);
print(stk);
return null;
}
};
children.addAll([
if (showSendingIndicator) _buildSendingIndicator(),
if (showReadList)
SizedBox.fromSize(
size: Size((widget.readList.length * 10.0) + 10, 17),
child: Padding(
padding: const EdgeInsets.only(left: 4.0),
child: _buildReadIndicator(),
),
),
if (showThreadReplyIndicator || showInChannel)
InkWell(
onTap: widget.onThreadTap != null ? onThreadTap : null,
child: Text(msg, style: widget.messageTheme?.replies),
),
if (showUsername)
Text(
widget.message.user.name,
style: widget.messageTheme.replies.copyWith(
color: widget.messageTheme.createdAt.color,
),
),
if (showTimeStamp)
Text(
Jiffy(widget.message.createdAt.toLocal()).jm,
style: widget.messageTheme.createdAt,
),
]);
}
if (widget.reverse) children = children.reversed.toList();
return Padding(
padding: EdgeInsets.only(left: leftPadding),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (!deleted && (showThreadReplyIndicator || showInChannel))
Container(
margin: EdgeInsets.only(
bottom: widget.messageTheme.replies.fontSize / 2),
child: CustomPaint(
size: const Size(16, 32),
painter: _ThreadReplyPainter(
color: widget.messageTheme.replyThreadColor,
),
),
),
...children.map(
(child) => Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: child,
),
),
].insertBetween(const SizedBox(width: 8.0)),
),
);
}
Widget _buildUrlAttachment() { Widget _buildUrlAttachment() {
var urlAttachment = widget.message.attachments var urlAttachment = widget.message.attachments
.firstWhere((element) => element.ogScrapeUrl != null); .firstWhere((element) => element.ogScrapeUrl != null);
@@ -394,87 +530,6 @@ class _MessageWidgetState extends State<MessageWidget> {
); );
} }
Padding _buildBottomRow(double leftPadding) {
return Padding(
padding: EdgeInsets.only(
left: leftPadding,
top: 2,
),
child: Row(
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
child: RichText(
text: TextSpan(
style: widget.messageTheme.createdAt,
children: <TextSpan>[
if (widget.showUsername)
TextSpan(
text: widget.message.user.name,
style: TextStyle(
fontWeight: FontWeight.bold,
color: widget.messageTheme.createdAt.color
.withOpacity(1)),
),
if (widget.message.createdAt != null && widget.showTimestamp)
TextSpan(
text: Jiffy(widget.message.createdAt.toLocal())
.format(' HH:mm'),
),
],
),
),
),
if (widget.showSendingIndicator == DisplayWidget.show)
_buildSendingIndicator(),
if (widget.readList?.isNotEmpty == true)
SizedBox.fromSize(
size: Size((widget.readList.length * 10.0) + 10, 17),
child: Transform(
alignment: Alignment.center,
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
child: Padding(
padding: const EdgeInsets.only(left: 4.0),
child: _buildReadIndicator(),
),
),
),
if (widget.message.isDeleted)
Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
StreamSvgIcon.eye(
color: Colors.black.withOpacity(0.5),
size: 16.0,
),
SizedBox(
width: 8.0,
),
Text(
'Only visible to you',
style: TextStyle(
color: Colors.black.withOpacity(0.5),
fontSize: 12.0,
),
),
],
),
),
),
],
),
);
}
bool get isGiphy => bool get isGiphy =>
widget.message.attachments?.any((element) => element.type == 'giphy') == widget.message.attachments?.any((element) => element.type == 'giphy') ==
true; true;
@@ -565,7 +620,7 @@ class _MessageWidgetState extends State<MessageWidget> {
true, true,
showReactions: widget.showReactions, showReactions: widget.showReactions,
showReply: showReply:
widget.showReplyIndicator && widget.onThreadTap != null, widget.showThreadReplyIndicator && widget.onThreadTap != null,
), ),
); );
}); });
@@ -715,38 +770,13 @@ class _MessageWidgetState extends State<MessageWidget> {
return; return;
} }
Widget _buildReplyIndicator(double leftPadding) {
return Padding(
padding: EdgeInsets.only(
left: leftPadding,
),
child: Transform(
transform: Matrix4.rotationY(widget.reverse ? pi : 0),
alignment: Alignment.center,
child: ReplyIndicator(
message: widget.message,
reversed: widget.reverse,
messageTheme: widget.messageTheme,
onTap: widget.onThreadTap != null
? () {
widget.onThreadTap(widget.message);
}
: null,
),
),
);
}
Widget _buildSendingIndicator() { Widget _buildSendingIndicator() {
return Padding( return Container(
padding: const EdgeInsets.only(right: 4.0), height: widget.messageTheme.createdAt.fontSize + 2,
child: Transform( width: widget.messageTheme.createdAt.fontSize + 2,
transform: Matrix4.rotationY(widget.reverse ? pi : 0), child: SendingIndicator(
alignment: Alignment.center, message: widget.message,
child: SendingIndicator( allRead: widget.allRead,
message: widget.message,
allRead: widget.allRead,
),
), ),
); );
} }
@@ -958,3 +988,32 @@ class _MessageWidgetState extends State<MessageWidget> {
} }
} }
} }
class _ThreadReplyPainter extends CustomPainter {
final Color color;
const _ThreadReplyPainter({@required this.color});
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = color ?? Color(0XFFDBDBDB)
..style = PaintingStyle.stroke
..strokeWidth = 1
..strokeCap = StrokeCap.round;
final path = Path()
..moveTo(0, 0)
..quadraticBezierTo(0, size.height * 0.38, 0, size.height * 0.50)
..quadraticBezierTo(
0,
size.height,
size.width,
size.height,
);
canvas.drawPath(path, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
-56
View File
@@ -1,56 +0,0 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:stream_chat_flutter/stream_chat_flutter.dart';
/// A reply button indicator
class ReplyIndicator extends StatelessWidget {
final Message message;
final VoidCallback onTap;
final bool reversed;
final MessageTheme messageTheme;
const ReplyIndicator({
Key key,
this.message,
this.onTap,
this.reversed = false,
this.messageTheme,
}) : super(key: key);
@override
Widget build(BuildContext context) {
var row = [
Text(
'Replies: ${message.replyCount}',
style: messageTheme?.replies,
),
Transform(
transform: Matrix4.rotationY(reversed ? 0 : pi),
alignment: Alignment.center,
child: Icon(
Icons.subdirectory_arrow_left,
color: Theme.of(context).brightness == Brightness.dark
? Colors.white12
: Colors.black12,
),
),
];
if (!reversed) {
row = row.reversed.toList();
}
return GestureDetector(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Row(
mainAxisSize: MainAxisSize.min,
children: row,
),
),
);
}
}
+13
View File
@@ -267,6 +267,19 @@ class StreamChannelState extends State<StreamChannel> {
return state; return state;
} }
///
Future<Message> getMessage(String messageId) async {
var message = channel.state.messages.firstWhere(
(it) => it.id == messageId,
orElse: () => null,
);
if (message == null) {
final response = await channel.getMessagesById([messageId]);
message = response.messages.first;
}
return message;
}
/// Reloads the channel with latest message /// Reloads the channel with latest message
Future<void> reloadChannel() => queryAtMessage(before: 30); Future<void> reloadChannel() => queryAtMessage(before: 30);
+15 -4
View File
@@ -191,6 +191,8 @@ class StreamChatThemeData {
this.ownMessageTheme.messageBackgroundColor, this.ownMessageTheme.messageBackgroundColor,
avatarTheme: ownMessageTheme?.avatarTheme ?? avatarTheme: ownMessageTheme?.avatarTheme ??
this.ownMessageTheme.avatarTheme, this.ownMessageTheme.avatarTheme,
replyThreadColor: ownMessageTheme?.replyThreadColor ??
this.ownMessageTheme.replyThreadColor,
) ?? ) ??
this.ownMessageTheme, this.ownMessageTheme,
otherMessageTheme: otherMessageTheme?.copyWith( otherMessageTheme: otherMessageTheme?.copyWith(
@@ -209,6 +211,8 @@ class StreamChatThemeData {
this.otherMessageTheme.messageBackgroundColor, this.otherMessageTheme.messageBackgroundColor,
avatarTheme: otherMessageTheme?.avatarTheme ?? avatarTheme: otherMessageTheme?.avatarTheme ??
this.otherMessageTheme.avatarTheme, this.otherMessageTheme.avatarTheme,
replyThreadColor: ownMessageTheme?.replyThreadColor ??
this.ownMessageTheme.replyThreadColor,
) ?? ) ??
this.otherMessageTheme, this.otherMessageTheme,
reactionIcons: reactionIcons ?? this.reactionIcons, reactionIcons: reactionIcons ?? this.reactionIcons,
@@ -297,16 +301,17 @@ class StreamChatThemeData {
color: isDark color: isDark
? Colors.white.withOpacity(.5) ? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5), : Colors.black.withOpacity(.5),
fontSize: 11, fontSize: 12,
), ),
replies: TextStyle( replies: TextStyle(
color: accentColor, color: accentColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w600,
fontSize: 12, fontSize: 12,
), ),
messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA), messageBackgroundColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
reactionsBackgroundColor: isDark ? Colors.black : Colors.white, reactionsBackgroundColor: isDark ? Colors.black : Colors.white,
reactionsBorderColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA), reactionsBorderColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
replyThreadColor: isDark ? Color(0xff191919) : Color(0xffEAEAEA),
avatarTheme: AvatarTheme( avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor( constraints: BoxConstraints.tightFor(
@@ -330,17 +335,19 @@ class StreamChatThemeData {
color: isDark color: isDark
? Colors.white.withOpacity(.5) ? Colors.white.withOpacity(.5)
: Colors.black.withOpacity(.5), : Colors.black.withOpacity(.5),
fontSize: 11, fontSize: 12,
), ),
replies: TextStyle( replies: TextStyle(
color: accentColor, color: accentColor,
fontWeight: FontWeight.bold, fontWeight: FontWeight.w600,
fontSize: 12, fontSize: 12,
), ),
messageLinks: TextStyle( messageLinks: TextStyle(
color: accentColor, color: accentColor,
), ),
messageBackgroundColor: isDark ? Colors.black : Colors.white, messageBackgroundColor: isDark ? Colors.black : Colors.white,
replyThreadColor:
isDark ? Colors.white.withAlpha(24) : Colors.black.withAlpha(24),
avatarTheme: AvatarTheme( avatarTheme: AvatarTheme(
borderRadius: BorderRadius.circular(20), borderRadius: BorderRadius.circular(20),
constraints: BoxConstraints.tightFor( constraints: BoxConstraints.tightFor(
@@ -455,6 +462,7 @@ class MessageTheme {
final Color messageBackgroundColor; final Color messageBackgroundColor;
final Color reactionsBackgroundColor; final Color reactionsBackgroundColor;
final Color reactionsBorderColor; final Color reactionsBorderColor;
final Color replyThreadColor;
final AvatarTheme avatarTheme; final AvatarTheme avatarTheme;
const MessageTheme({ const MessageTheme({
@@ -465,6 +473,7 @@ class MessageTheme {
this.messageBackgroundColor, this.messageBackgroundColor,
this.reactionsBackgroundColor, this.reactionsBackgroundColor,
this.reactionsBorderColor, this.reactionsBorderColor,
this.replyThreadColor,
this.avatarTheme, this.avatarTheme,
this.createdAt, this.createdAt,
}); });
@@ -479,6 +488,7 @@ class MessageTheme {
AvatarTheme avatarTheme, AvatarTheme avatarTheme,
Color reactionsBackgroundColor, Color reactionsBackgroundColor,
Color reactionsBorderColor, Color reactionsBorderColor,
Color replyThreadColor,
}) => }) =>
MessageTheme( MessageTheme(
messageText: messageText ?? this.messageText, messageText: messageText ?? this.messageText,
@@ -492,6 +502,7 @@ class MessageTheme {
reactionsBackgroundColor: reactionsBackgroundColor:
reactionsBackgroundColor ?? this.reactionsBackgroundColor, reactionsBackgroundColor ?? this.reactionsBackgroundColor,
reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor, reactionsBorderColor: reactionsBorderColor ?? this.reactionsBorderColor,
replyThreadColor: replyThreadColor ?? this.replyThreadColor,
); );
} }
+9
View File
@@ -92,3 +92,12 @@ Future<bool> showConfirmationDialog(
/// Get random png with initials /// Get random png with initials
String getRandomPicUrl(User user) => String getRandomPicUrl(User user) =>
'https://getstream.io/random_png/?id=${user.id}&name=${user.name}'; 'https://getstream.io/random_png/?id=${user.id}&name=${user.name}';
/// List extension
extension ListX<T> on List<T> {
/// Insert any item<T> inBetween the list items
List<T> insertBetween(T item) => expand((e) sync* {
yield item;
yield e;
}).skip(1).toList(growable: false);
}
-1
View File
@@ -21,7 +21,6 @@ export 'src/message_list_view.dart';
export 'src/message_text.dart'; export 'src/message_text.dart';
export 'src/message_widget.dart'; export 'src/message_widget.dart';
export 'src/reaction_picker.dart'; export 'src/reaction_picker.dart';
export 'src/reply_indicator.dart';
export 'src/sending_indicator.dart'; export 'src/sending_indicator.dart';
export 'src/stream_channel.dart'; export 'src/stream_channel.dart';
export 'src/stream_chat.dart'; export 'src/stream_chat.dart';