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
+22 -2
View File
@@ -95,7 +95,13 @@ class MessageActionsModal extends StatelessWidget {
messageTheme: messageTheme, messageTheme: messageTheme,
), ),
), ),
IgnorePointer( TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
builder: (context, val, snapshot) {
return Transform.scale(
scale: val,
child: IgnorePointer(
child: MessageWidget( child: MessageWidget(
key: Key('MessageWidget'), key: Key('MessageWidget'),
reverse: reverse, reverse: reverse,
@@ -116,10 +122,21 @@ class MessageActionsModal extends StatelessWidget {
shape: messageShape, shape: messageShape,
), ),
), ),
);
}
),
SizedBox( SizedBox(
height: 8, height: 8,
), ),
Padding( TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
builder: (context, val, wid) {
return Transform(
transform: Matrix4.identity()..scale(val)..rotateZ(-1.0 + val),
alignment: Alignment.topRight,
child: Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: 48.0, horizontal: 48.0,
), ),
@@ -147,6 +164,9 @@ class MessageActionsModal extends StatelessWidget {
).toList(), ).toList(),
), ),
), ),
),
);
}
) )
], ],
), ),
+69 -10
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,11 +21,39 @@ 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) {
reactionIcons.forEach((element) {
animations.add(
EzAnimation.sequence([
SequenceItem(0.0, 1.4),
SequenceItem(1.4, 1.0),
], Duration(milliseconds: 500)),
);
});
triggerAnimations();
}
return TweenAnimationBuilder<double>(
tween: Tween(begin: 0.0, end: 1.0),
curve: Curves.easeInOutExpo,
duration: Duration(milliseconds: 500),
builder: (context, val, wid) {
return Transform.scale(
scale: val,
child: Material(
color: widget.messageTheme.reactionsBackgroundColor,
clipBehavior: Clip.hardEdge, clipBehavior: Clip.hardEdge,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(24), borderRadius: BorderRadius.circular(24),
@@ -32,22 +63,39 @@ class ReactionPicker extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: reactionIcons.map((reactionIcon) { children: reactionIcons.map((reactionIcon) {
final ownReactionIndex = message.ownReactions?.indexWhere( final ownReactionIndex = widget.message.ownReactions
(reaction) => reaction.type == reactionIcon.type) ?? ?.indexWhere((reaction) =>
reaction.type == reactionIcon.type) ??
-1; -1;
var index = reactionIcons.indexOf(reactionIcon);
return IconButton( return IconButton(
iconSize: 24, iconSize: 24,
icon: Icon( 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, reactionIcon.iconData,
size: animations[index].value * 24.0,
color: ownReactionIndex != -1 color: ownReactionIndex != -1
? StreamChatTheme.of(context).accentColor ? StreamChatTheme.of(context).accentColor
: Theme.of(context).iconTheme.color.withOpacity(.5), : Theme.of(context)
.iconTheme
.color
.withOpacity(.5),
), ),
);
}),
onPressed: () { onPressed: () {
if (ownReactionIndex != -1) { if (ownReactionIndex != -1) {
removeReaction( removeReaction(
context, context,
message.ownReactions[ownReactionIndex], widget.message.ownReactions[ownReactionIndex],
); );
} else { } else {
sendReaction( sendReaction(
@@ -59,18 +107,29 @@ class ReactionPicker extends StatelessWidget {
); );
}).toList(), }).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: