add custom clipper to messagewidget
This commit is contained in:
@@ -91,7 +91,7 @@ class MyApp extends StatelessWidget {
|
||||
return MaterialApp(
|
||||
theme: ThemeData.light(),
|
||||
darkTheme: ThemeData.dark(),
|
||||
themeMode: ThemeMode.system,
|
||||
themeMode: ThemeMode.dark,
|
||||
home: Container(
|
||||
child: StreamChat(
|
||||
client: client,
|
||||
|
||||
@@ -157,9 +157,6 @@ class ChannelHeader extends StatelessWidget implements PreferredSizeWidget {
|
||||
child: Icon(
|
||||
Icons.arrow_back_ios,
|
||||
size: 15,
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.white
|
||||
: Colors.black,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -476,7 +476,6 @@ class _MessageInputState extends State<MessageInput> {
|
||||
child: Icon(
|
||||
Icons.close,
|
||||
size: 15,
|
||||
color: Colors.black,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -71,6 +71,8 @@ class MessageListView extends StatefulWidget {
|
||||
this.onMessageActions,
|
||||
this.attachmentBuilders,
|
||||
this.dateDividerBuilder,
|
||||
this.showAvatar = true,
|
||||
this.customClipperBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Function used to build a custom message widget
|
||||
@@ -110,6 +112,13 @@ class MessageListView extends StatefulWidget {
|
||||
/// Builder used to render date dividers
|
||||
final Widget Function(DateTime) dateDividerBuilder;
|
||||
|
||||
/// if true shows the user avatar
|
||||
final bool showAvatar;
|
||||
|
||||
/// Custom clipper applied to the message bubble
|
||||
final CustomClipper Function(BuildContext, Message, int index)
|
||||
customClipperBuilder;
|
||||
|
||||
@override
|
||||
_MessageListViewState createState() => _MessageListViewState();
|
||||
}
|
||||
@@ -171,6 +180,14 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
onMessageActions: widget.onMessageActions,
|
||||
attachmentBuilders: widget.attachmentBuilders,
|
||||
showAvatar: widget.showAvatar,
|
||||
customClipperBuilder: (message) {
|
||||
return widget.customClipperBuilder(
|
||||
context,
|
||||
message,
|
||||
i,
|
||||
);
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||
@@ -235,6 +252,12 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
onMessageActions: widget.onMessageActions,
|
||||
attachmentBuilders: widget.attachmentBuilders,
|
||||
showAvatar: widget.showAvatar,
|
||||
customClipperBuilder: (message) {
|
||||
if (widget.customClipperBuilder != null) {
|
||||
return widget.customClipperBuilder(context, message, i);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -308,7 +331,8 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
if (widget.messageBuilder != null) {
|
||||
messageWidget = Builder(
|
||||
key: ValueKey<String>('MESSAGE-${message.id}'),
|
||||
builder: (_) => widget.messageBuilder(context, message, 0),
|
||||
builder: (_) =>
|
||||
widget.messageBuilder(context, message, _messages.length - 1),
|
||||
);
|
||||
} else {
|
||||
messageWidget = MessageWidget(
|
||||
@@ -323,6 +347,11 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
onMessageActions: widget.onMessageActions,
|
||||
attachmentBuilders: widget.attachmentBuilders,
|
||||
showAvatar: widget.showAvatar,
|
||||
customClipperBuilder: (message) {
|
||||
return widget.customClipperBuilder(
|
||||
context, message, _messages.length - 1);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -364,6 +393,10 @@ class _MessageListViewState extends State<MessageListView> {
|
||||
onUserAvatarTap: widget.onUserAvatarTap,
|
||||
onMessageActions: widget.onMessageActions,
|
||||
attachmentBuilders: widget.attachmentBuilders,
|
||||
showAvatar: widget.showAvatar,
|
||||
customClipperBuilder: (message) {
|
||||
return widget.customClipperBuilder(context, message, 0);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+63
-42
@@ -48,6 +48,8 @@ class MessageWidget extends StatefulWidget {
|
||||
this.showOtherMessageUsername = false,
|
||||
this.showVideoFullScreen = true,
|
||||
this.attachmentBuilders,
|
||||
this.showAvatar = true,
|
||||
this.customClipperBuilder,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Function called on mention tap
|
||||
@@ -83,6 +85,12 @@ class MessageWidget extends StatefulWidget {
|
||||
/// Map that defines a builder for an attachment type
|
||||
final Map<String, AttachmentBuilder> attachmentBuilders;
|
||||
|
||||
/// if true shows the user avatar
|
||||
final bool showAvatar;
|
||||
|
||||
/// Custom clipper applied to the message bubble
|
||||
final CustomClipper Function(Message) customClipperBuilder;
|
||||
|
||||
@override
|
||||
_MessageWidgetState createState() => _MessageWidgetState();
|
||||
}
|
||||
@@ -130,7 +138,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
),
|
||||
_isNextUser
|
||||
? Container(
|
||||
width: 40,
|
||||
width: widget.showAvatar ? 40 : 8,
|
||||
)
|
||||
: _buildUserAvatar(),
|
||||
]);
|
||||
@@ -164,11 +172,13 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
right: _isMyMessage ? 0 : 8.0,
|
||||
),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
UserAvatar(
|
||||
user: widget.message.user,
|
||||
onTap: widget.onUserAvatarTap,
|
||||
),
|
||||
if (widget.showAvatar)
|
||||
UserAvatar(
|
||||
user: widget.message.user,
|
||||
onTap: widget.onUserAvatarTap,
|
||||
),
|
||||
if (_isMyMessage && widget.nextMessage == null)
|
||||
SendingIndicator(
|
||||
message: widget.message,
|
||||
@@ -466,7 +476,7 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
);
|
||||
}
|
||||
|
||||
Padding _buildMessageText(
|
||||
Widget _buildMessageText(
|
||||
int nOfAttachmentWidgets,
|
||||
String text,
|
||||
BuildContext context,
|
||||
@@ -476,46 +486,57 @@ class _MessageWidgetState extends State<MessageWidget>
|
||||
right: _isMyMessage ? 0.0 : 8.0,
|
||||
left: _isMyMessage ? 8.0 : 0.0,
|
||||
),
|
||||
child: Container(
|
||||
decoration:
|
||||
_buildBoxDecoration(_isLastUser || nOfAttachmentWidgets > 0),
|
||||
padding: EdgeInsets.all(10),
|
||||
constraints: BoxConstraints.loose(
|
||||
Size.fromWidth(MediaQuery.of(context).size.width * 0.7),
|
||||
),
|
||||
child: _buildSendingError(
|
||||
MarkdownBody(
|
||||
data: text,
|
||||
onTapLink: (link) {
|
||||
if (link.startsWith('@')) {
|
||||
final mentionedUser = widget.message.mentionedUsers.firstWhere(
|
||||
(u) => '@${u.name.replaceAll(' ', '')}' == link,
|
||||
orElse: () => null,
|
||||
);
|
||||
child: ClipPath(
|
||||
clipper: widget.customClipperBuilder != null
|
||||
? widget.customClipperBuilder(widget.message)
|
||||
: null,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: Container(
|
||||
decoration:
|
||||
_buildBoxDecoration(_isLastUser || nOfAttachmentWidgets > 0)
|
||||
.copyWith(
|
||||
borderRadius: widget.customClipperBuilder != null
|
||||
? BorderRadius.circular(0)
|
||||
: null),
|
||||
padding: EdgeInsets.all(10),
|
||||
constraints: BoxConstraints.loose(
|
||||
Size.fromWidth(MediaQuery.of(context).size.width * 0.7),
|
||||
),
|
||||
child: _buildSendingError(
|
||||
MarkdownBody(
|
||||
data: text,
|
||||
onTapLink: (link) {
|
||||
if (link.startsWith('@')) {
|
||||
final mentionedUser =
|
||||
widget.message.mentionedUsers.firstWhere(
|
||||
(u) => '@${u.name.replaceAll(' ', '')}' == link,
|
||||
orElse: () => null,
|
||||
);
|
||||
|
||||
if (widget.onMentionTap != null) {
|
||||
widget.onMentionTap(mentionedUser);
|
||||
if (widget.onMentionTap != null) {
|
||||
widget.onMentionTap(mentionedUser);
|
||||
} else {
|
||||
print('tap on ${mentionedUser.name}');
|
||||
}
|
||||
} else {
|
||||
print('tap on ${mentionedUser.name}');
|
||||
launchURL(context, link);
|
||||
}
|
||||
} else {
|
||||
launchURL(context, link);
|
||||
}
|
||||
},
|
||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||
Theme.of(context).copyWith(
|
||||
textTheme: Theme.of(context).textTheme.apply(
|
||||
bodyColor: _messageTheme.messageText.color,
|
||||
decoration: _messageTheme.messageText.decoration,
|
||||
decorationColor:
|
||||
_messageTheme.messageText.decorationColor,
|
||||
decorationStyle:
|
||||
_messageTheme.messageText.decorationStyle,
|
||||
fontFamily: _messageTheme.messageText.fontFamily,
|
||||
),
|
||||
},
|
||||
styleSheet: MarkdownStyleSheet.fromTheme(
|
||||
Theme.of(context).copyWith(
|
||||
textTheme: Theme.of(context).textTheme.apply(
|
||||
bodyColor: _messageTheme.messageText.color,
|
||||
decoration: _messageTheme.messageText.decoration,
|
||||
decorationColor:
|
||||
_messageTheme.messageText.decorationColor,
|
||||
decorationStyle:
|
||||
_messageTheme.messageText.decorationStyle,
|
||||
fontFamily: _messageTheme.messageText.fontFamily,
|
||||
),
|
||||
),
|
||||
).copyWith(
|
||||
p: _messageTheme.messageText,
|
||||
),
|
||||
).copyWith(
|
||||
p: _messageTheme.messageText,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -73,9 +73,11 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
||||
builder: (context) {
|
||||
final materialTheme = Theme.of(context);
|
||||
final isDark = materialTheme.brightness == Brightness.dark;
|
||||
final streamTheme = StreamChatTheme.of(context);
|
||||
return Theme(
|
||||
data: materialTheme.copyWith(
|
||||
accentColor: StreamChatTheme.of(context).accentColor,
|
||||
primaryIconTheme: streamTheme.primaryIconTheme,
|
||||
accentColor: streamTheme.accentColor,
|
||||
scaffoldBackgroundColor: isDark ? Colors.black : Colors.white,
|
||||
backgroundColor: isDark ? Colors.black : Colors.white,
|
||||
),
|
||||
@@ -113,6 +115,7 @@ class StreamChatState extends State<StreamChat> with WidgetsBindingObserver {
|
||||
final theme = defaultTheme.copyWith(
|
||||
primaryColor: themeData?.primaryColor,
|
||||
defaultChannelImage: themeData?.defaultChannelImage,
|
||||
primaryIconTheme: themeData?.primaryIconTheme,
|
||||
defaultUserImage: themeData?.defaultUserImage,
|
||||
channelTheme: defaultTheme.channelTheme.copyWith(
|
||||
channelHeaderTheme:
|
||||
|
||||
@@ -57,6 +57,9 @@ class StreamChatThemeData {
|
||||
/// The widget that will be built when the user image is unavailable
|
||||
final Widget Function(BuildContext, User) defaultUserImage;
|
||||
|
||||
/// Primary icon theme
|
||||
final IconThemeData primaryIconTheme;
|
||||
|
||||
/// Create a theme from scratch
|
||||
StreamChatThemeData({
|
||||
this.primaryColor,
|
||||
@@ -68,6 +71,7 @@ class StreamChatThemeData {
|
||||
this.ownMessageTheme,
|
||||
this.defaultChannelImage,
|
||||
this.defaultUserImage,
|
||||
this.primaryIconTheme,
|
||||
});
|
||||
|
||||
/// Create a theme from a Material [Theme]
|
||||
@@ -76,6 +80,7 @@ class StreamChatThemeData {
|
||||
|
||||
return defaultTheme.copyWith(
|
||||
accentColor: theme.accentColor,
|
||||
primaryIconTheme: theme.primaryIconTheme,
|
||||
primaryColor: theme.colorScheme.primary,
|
||||
secondaryColor: theme.colorScheme.secondary,
|
||||
channelTheme: ChannelTheme(
|
||||
@@ -108,10 +113,12 @@ class StreamChatThemeData {
|
||||
MessageTheme otherMessageTheme,
|
||||
Widget Function(BuildContext, Channel) defaultChannelImage,
|
||||
Widget Function(BuildContext, User) defaultUserImage,
|
||||
IconThemeData primaryIconTheme,
|
||||
}) =>
|
||||
StreamChatThemeData(
|
||||
primaryColor: primaryColor ?? this.primaryColor,
|
||||
secondaryColor: secondaryColor ?? this.secondaryColor,
|
||||
primaryIconTheme: primaryIconTheme ?? this.primaryIconTheme,
|
||||
accentColor: accentColor ?? this.accentColor,
|
||||
defaultChannelImage: defaultChannelImage ?? this.defaultChannelImage,
|
||||
defaultUserImage: defaultUserImage ?? this.defaultUserImage,
|
||||
@@ -183,6 +190,8 @@ class StreamChatThemeData {
|
||||
return StreamChatThemeData(
|
||||
accentColor: accentColor,
|
||||
primaryColor: isDark ? Colors.black : Colors.white,
|
||||
primaryIconTheme:
|
||||
IconThemeData(color: isDark ? Colors.white : Colors.black),
|
||||
defaultChannelImage: (context, channel) => SizedBox(),
|
||||
defaultUserImage: (context, user) => Center(
|
||||
child: Text(
|
||||
|
||||
Reference in New Issue
Block a user