add custom clipper to messagewidget

This commit is contained in:
Salvatore Giordano
2020-04-25 14:10:16 +02:00
parent 3ea81c2519
commit 3d72ea699c
7 changed files with 111 additions and 49 deletions
+1 -1
View File
@@ -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,
-3
View File
@@ -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,
),
),
);
-1
View File
@@ -476,7 +476,6 @@ class _MessageInputState extends State<MessageInput> {
child: Icon(
Icons.close,
size: 15,
color: Colors.black,
),
),
),
+34 -1
View File
@@ -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
View File
@@ -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,
),
),
),
+4 -1
View File
@@ -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:
+9
View File
@@ -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(