feat: Added reaction modal animations

This commit is contained in:
Deven Joshi
2020-11-20 13:26:26 +05:30
parent 26e8ffeae3
commit 404ec4c89d
3 changed files with 168 additions and 88 deletions
+68 -48
View File
@@ -95,58 +95,78 @@ class MessageActionsModal extends StatelessWidget {
messageTheme: messageTheme, messageTheme: messageTheme,
), ),
), ),
IgnorePointer( TweenAnimationBuilder<double>(
child: MessageWidget( tween: Tween(begin: 0.0, end: 1.0),
key: Key('MessageWidget'), duration: Duration(milliseconds: 300),
reverse: reverse, builder: (context, val, snapshot) {
message: message.copyWith( return Transform.scale(
text: message.text.length > 200 scale: val,
? '${message.text.substring(0, 200)}...' child: IgnorePointer(
: message.text, child: MessageWidget(
), key: Key('MessageWidget'),
messageTheme: messageTheme, reverse: reverse,
showReactions: false, message: message.copyWith(
showUsername: false, text: message.text.length > 200
showReplyIndicator: false, ? '${message.text.substring(0, 200)}...'
showUserAvatar: showUserAvatar, : message.text,
showTimestamp: false, ),
translateUserAvatar: false, messageTheme: messageTheme,
showReactionPickerIndicator: true, showReactions: false,
showSendingIndicator: DisplayWidget.gone, showUsername: false,
shape: messageShape, showReplyIndicator: false,
), showUserAvatar: showUserAvatar,
showTimestamp: false,
translateUserAvatar: false,
showReactionPickerIndicator: true,
showSendingIndicator: DisplayWidget.gone,
shape: messageShape,
),
),
);
}
), ),
SizedBox( SizedBox(
height: 8, height: 8,
), ),
Padding( TweenAnimationBuilder<double>(
padding: const EdgeInsets.symmetric( tween: Tween(begin: 0.0, end: 1.0),
horizontal: 48.0, duration: Duration(milliseconds: 300),
), curve: Curves.easeInOut,
child: Material( builder: (context, val, wid) {
clipBehavior: Clip.hardEdge, return Transform(
shape: RoundedRectangleBorder( transform: Matrix4.identity()..scale(val)..rotateZ(-1.0 + val),
borderRadius: BorderRadius.circular(16), alignment: Alignment.topRight,
), child: Padding(
child: Column( padding: const EdgeInsets.symmetric(
crossAxisAlignment: CrossAxisAlignment.stretch, horizontal: 48.0,
children: ListTile.divideTiles( ),
context: context, child: Material(
tiles: [ clipBehavior: Clip.hardEdge,
if (showReply && shape: RoundedRectangleBorder(
(message.status == borderRadius: BorderRadius.circular(16),
MessageSendingStatus.SENT || ),
message.status == null) && child: Column(
message.parentId == null) crossAxisAlignment: CrossAxisAlignment.stretch,
_buildReplyButton(context), children: ListTile.divideTiles(
if (showEditMessage) _buildEditMessage(context), context: context,
if (showDeleteMessage) tiles: [
_buildDeleteButton(context), if (showReply &&
if (showCopyMessage) _buildCopyButton(context), (message.status ==
], MessageSendingStatus.SENT ||
).toList(), message.status == null) &&
), message.parentId == null)
), _buildReplyButton(context),
if (showEditMessage) _buildEditMessage(context),
if (showDeleteMessage)
_buildDeleteButton(context),
if (showCopyMessage) _buildCopyButton(context),
],
).toList(),
),
),
),
);
}
) )
], ],
), ),
+99 -40
View File
@@ -1,4 +1,6 @@
import 'package:ezanimation/ezanimation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/physics.dart';
import '../stream_chat_flutter.dart'; import '../stream_chat_flutter.dart';
@@ -8,7 +10,8 @@ import '../stream_chat_flutter.dart';
/// It shows a reaction picker /// It shows a reaction picker
/// ///
/// Usually you don't use this widget as it's one of the default widgets used by [MessageWidget.onMessageActions]. /// Usually you don't use this widget as it's one of the default widgets used by [MessageWidget.onMessageActions].
class ReactionPicker extends StatelessWidget {
class ReactionPicker extends StatefulWidget {
const ReactionPicker({ const ReactionPicker({
Key key, Key key,
@required this.message, @required this.message,
@@ -18,59 +21,115 @@ class ReactionPicker extends StatelessWidget {
final Message message; final Message message;
final MessageTheme messageTheme; final MessageTheme messageTheme;
@override
_ReactionPickerState createState() => _ReactionPickerState();
}
class _ReactionPickerState extends State<ReactionPicker> {
List<EzAnimation> animations = [];
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final reactionIcons = StreamChatTheme.of(context).reactionIcons; final reactionIcons = StreamChatTheme.of(context).reactionIcons;
return Material(
color: messageTheme.reactionsBackgroundColor, if (animations.isEmpty && reactionIcons.isNotEmpty) {
clipBehavior: Clip.hardEdge, reactionIcons.forEach((element) {
shape: RoundedRectangleBorder( animations.add(
borderRadius: BorderRadius.circular(24), EzAnimation.sequence([
), SequenceItem(0.0, 1.4),
child: Row( SequenceItem(1.4, 1.0),
crossAxisAlignment: CrossAxisAlignment.start, ], Duration(milliseconds: 500)),
mainAxisAlignment: MainAxisAlignment.end, );
mainAxisSize: MainAxisSize.min, });
children: reactionIcons.map((reactionIcon) {
final ownReactionIndex = message.ownReactions?.indexWhere( triggerAnimations();
(reaction) => reaction.type == reactionIcon.type) ?? }
-1;
return IconButton( return TweenAnimationBuilder<double>(
iconSize: 24, tween: Tween(begin: 0.0, end: 1.0),
icon: Icon( curve: Curves.easeInOutExpo,
reactionIcon.iconData, duration: Duration(milliseconds: 500),
color: ownReactionIndex != -1 builder: (context, val, wid) {
? StreamChatTheme.of(context).accentColor return Transform.scale(
: Theme.of(context).iconTheme.color.withOpacity(.5), scale: val,
child: Material(
color: widget.messageTheme.reactionsBackgroundColor,
clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min,
children: reactionIcons.map((reactionIcon) {
final ownReactionIndex = widget.message.ownReactions
?.indexWhere((reaction) =>
reaction.type == reactionIcon.type) ??
-1;
var index = reactionIcons.indexOf(reactionIcon);
return IconButton(
iconSize: 24,
icon: AnimatedBuilder(
animation: animations[index],
builder: (context, val) {
return Transform(
transform: Matrix4.identity()
..scale(animations[index].value,
animations[index].value)
..rotateZ(1.0 - animations[index].value),
child: Icon(
reactionIcon.iconData,
size: animations[index].value * 24.0,
color: ownReactionIndex != -1
? StreamChatTheme.of(context).accentColor
: Theme.of(context)
.iconTheme
.color
.withOpacity(.5),
),
);
}),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(
context,
widget.message.ownReactions[ownReactionIndex],
);
} else {
sendReaction(
context,
reactionIcon.type,
);
}
},
);
}).toList(),
),
), ),
onPressed: () {
if (ownReactionIndex != -1) {
removeReaction(
context,
message.ownReactions[ownReactionIndex],
);
} else {
sendReaction(
context,
reactionIcon.type,
);
}
},
); );
}).toList(), });
), }
);
void triggerAnimations() async {
for (var a in animations) {
a.start();
await Future.delayed(Duration(milliseconds: 100));
}
} }
/// Add a reaction to the message /// Add a reaction to the message
void sendReaction(BuildContext context, String reactionType) { void sendReaction(BuildContext context, String reactionType) {
StreamChannel.of(context).channel.sendReaction(message, reactionType); StreamChannel.of(context)
.channel
.sendReaction(widget.message, reactionType);
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
/// Remove a reaction from the message /// Remove a reaction from the message
void removeReaction(BuildContext context, Reaction reaction) { void removeReaction(BuildContext context, Reaction reaction) {
StreamChannel.of(context).channel.deleteReaction(message, reaction); StreamChannel.of(context).channel.deleteReaction(widget.message, reaction);
Navigator.of(context).pop(); Navigator.of(context).pop();
} }
} }
+1
View File
@@ -38,6 +38,7 @@ dependencies:
media_gallery: ^0.1.5 media_gallery: ^0.1.5
permission_handler: ^5.0.1+1 permission_handler: ^5.0.1+1
transparent_image: ^1.0.0 transparent_image: ^1.0.0
ezanimation: ^0.4.0
flutter: flutter:
assets: assets: