From 404ec4c89d43bb9b39505be13931e7c0d39aae87 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 13:26:26 +0530 Subject: [PATCH 01/10] feat: Added reaction modal animations --- lib/src/message_actions_modal.dart | 116 ++++++++++++++---------- lib/src/reaction_picker.dart | 139 ++++++++++++++++++++--------- pubspec.yaml | 1 + 3 files changed, 168 insertions(+), 88 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 16c48e25..049f1be7 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -95,58 +95,78 @@ class MessageActionsModal extends StatelessWidget { messageTheme: messageTheme, ), ), - IgnorePointer( - child: MessageWidget( - key: Key('MessageWidget'), - reverse: reverse, - message: message.copyWith( - text: message.text.length > 200 - ? '${message.text.substring(0, 200)}...' - : message.text, - ), - messageTheme: messageTheme, - showReactions: false, - showUsername: false, - showReplyIndicator: false, - showUserAvatar: showUserAvatar, - showTimestamp: false, - translateUserAvatar: false, - showReactionPickerIndicator: true, - showSendingIndicator: DisplayWidget.gone, - shape: messageShape, - ), + TweenAnimationBuilder( + 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( + key: Key('MessageWidget'), + reverse: reverse, + message: message.copyWith( + text: message.text.length > 200 + ? '${message.text.substring(0, 200)}...' + : message.text, + ), + messageTheme: messageTheme, + showReactions: false, + showUsername: false, + showReplyIndicator: false, + showUserAvatar: showUserAvatar, + showTimestamp: false, + translateUserAvatar: false, + showReactionPickerIndicator: true, + showSendingIndicator: DisplayWidget.gone, + shape: messageShape, + ), + ), + ); + } ), SizedBox( height: 8, ), - Padding( - padding: const EdgeInsets.symmetric( - horizontal: 48.0, - ), - child: Material( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: ListTile.divideTiles( - context: context, - tiles: [ - if (showReply && - (message.status == - MessageSendingStatus.SENT || - message.status == null) && - message.parentId == null) - _buildReplyButton(context), - if (showEditMessage) _buildEditMessage(context), - if (showDeleteMessage) - _buildDeleteButton(context), - if (showCopyMessage) _buildCopyButton(context), - ], - ).toList(), - ), - ), + TweenAnimationBuilder( + 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( + horizontal: 48.0, + ), + child: Material( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: ListTile.divideTiles( + context: context, + tiles: [ + if (showReply && + (message.status == + MessageSendingStatus.SENT || + message.status == null) && + message.parentId == null) + _buildReplyButton(context), + if (showEditMessage) _buildEditMessage(context), + if (showDeleteMessage) + _buildDeleteButton(context), + if (showCopyMessage) _buildCopyButton(context), + ], + ).toList(), + ), + ), + ), + ); + } ) ], ), diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index e7d302e6..1946e9ff 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -1,4 +1,6 @@ +import 'package:ezanimation/ezanimation.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/physics.dart'; import '../stream_chat_flutter.dart'; @@ -8,7 +10,8 @@ import '../stream_chat_flutter.dart'; /// It shows a reaction picker /// /// 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({ Key key, @required this.message, @@ -18,59 +21,115 @@ class ReactionPicker extends StatelessWidget { final Message message; final MessageTheme messageTheme; + @override + _ReactionPickerState createState() => _ReactionPickerState(); +} + +class _ReactionPickerState extends State { + List animations = []; + @override Widget build(BuildContext context) { final reactionIcons = StreamChatTheme.of(context).reactionIcons; - return Material( - color: 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 = message.ownReactions?.indexWhere( - (reaction) => reaction.type == reactionIcon.type) ?? - -1; - return IconButton( - iconSize: 24, - icon: Icon( - reactionIcon.iconData, - color: ownReactionIndex != -1 - ? StreamChatTheme.of(context).accentColor - : Theme.of(context).iconTheme.color.withOpacity(.5), + + 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( + 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, + 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 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(); } /// Remove a reaction from the message 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(); } } diff --git a/pubspec.yaml b/pubspec.yaml index 1716bab1..689a54b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,6 +38,7 @@ dependencies: media_gallery: ^0.1.5 permission_handler: ^5.0.1+1 transparent_image: ^1.0.0 + ezanimation: ^0.4.0 flutter: assets: From f9231e93b3059be68cece526288555d5e63e7cba Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 13:38:15 +0530 Subject: [PATCH 02/10] feat: Added reaction list modal animations --- lib/src/message_reactions_modal.dart | 146 +++++++++++++++------------ 1 file changed, 82 insertions(+), 64 deletions(-) diff --git a/lib/src/message_reactions_modal.dart b/lib/src/message_reactions_modal.dart index d68bd769..db5d8710 100644 --- a/lib/src/message_reactions_modal.dart +++ b/lib/src/message_reactions_modal.dart @@ -86,27 +86,35 @@ class MessageReactionsModal extends StatelessWidget { messageTheme: messageTheme, ), ), - IgnorePointer( - child: MessageWidget( - key: Key('MessageWidget'), - reverse: reverse, - message: message.copyWith( - text: message.text.length > 200 - ? '${message.text.substring(0, 200)}...' - : message.text, - ), - messageTheme: messageTheme, - showReactions: false, - showUsername: false, - showUserAvatar: showUserAvatar, - showReplyIndicator: false, - showTimestamp: false, - translateUserAvatar: false, - showSendingIndicator: DisplayWidget.gone, - shape: messageShape, - showReactionPickerIndicator: true, - ), - ), + TweenAnimationBuilder( + 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( + key: Key('MessageWidget'), + reverse: reverse, + message: message.copyWith( + text: message.text.length > 200 + ? '${message.text.substring(0, 200)}...' + : message.text, + ), + messageTheme: messageTheme, + showReactions: false, + showUsername: false, + showUserAvatar: showUserAvatar, + showReplyIndicator: false, + showTimestamp: false, + translateUserAvatar: false, + showSendingIndicator: DisplayWidget.gone, + shape: messageShape, + showReactionPickerIndicator: true, + ), + ), + ); + }), SizedBox( height: 16, ), @@ -178,50 +186,60 @@ class MessageReactionsModal extends StatelessWidget { BuildContext context, ) { final isCurrentUser = reaction.user.id == currentUser.id; - return ConstrainedBox( - constraints: BoxConstraints.loose(Size( - 64, - 98, - )), - child: Column( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Stack( - children: [ - UserAvatar( - onTap: onUserAvatarTap, - user: reaction.user, - constraints: BoxConstraints.tightFor( - height: 64, - width: 64, - ), - borderRadius: BorderRadius.circular(32), - ), - Positioned( - child: Align( - alignment: Alignment.centerLeft, - child: ReactionBubble( - reactions: [reaction], - borderColor: messageTheme.reactionsBorderColor, - backgroundColor: messageTheme.reactionsBackgroundColor, - highlightOwnReactions: false, + return TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOut, + builder: (context, val, snapshot) { + return Transform.scale( + scale: val, + child: ConstrainedBox( + constraints: BoxConstraints.loose(Size( + 64, + 98, + )), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Stack( + children: [ + UserAvatar( + onTap: onUserAvatarTap, + user: reaction.user, + constraints: BoxConstraints.tightFor( + height: 64, + width: 64, + ), + borderRadius: BorderRadius.circular(32), + ), + Positioned( + child: Align( + alignment: Alignment.centerLeft, + child: ReactionBubble( + reactions: [reaction], + borderColor: messageTheme.reactionsBorderColor, + backgroundColor: + messageTheme.reactionsBackgroundColor, + highlightOwnReactions: false, + ), + ), + bottom: 4, + left: isCurrentUser ? 0 : null, + right: isCurrentUser ? 0 : null, + ), + ], ), - ), - bottom: 4, - left: isCurrentUser ? 0 : null, - right: isCurrentUser ? 0 : null, + Text( + reaction.user.name, + style: Theme.of(context).textTheme.subtitle2, + textAlign: TextAlign.center, + ), + ], ), - ], - ), - Text( - reaction.user.name, - style: Theme.of(context).textTheme.subtitle2, - textAlign: TextAlign.center, - ), - ], - ), - ); + ), + ); + }); } } From c70c49d54d33c6f40404579ba1c382ca3d333dce Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 15:07:42 +0530 Subject: [PATCH 03/10] feat: Added animations for giphy and mentions --- lib/src/message_input.dart | 302 ++++++++++++++++++++----------------- 1 file changed, 165 insertions(+), 137 deletions(-) diff --git a/lib/src/message_input.dart b/lib/src/message_input.dart index cd96119a..01c531de 100644 --- a/lib/src/message_input.dart +++ b/lib/src/message_input.dart @@ -536,86 +536,101 @@ class MessageInputState extends State { bottom: size.height + MediaQuery.of(context).viewInsets.bottom, left: 0, right: 0, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Card( - elevation: 2.0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - color: StreamChatTheme.of(context).primaryColor, - clipBehavior: Clip.antiAlias, - child: Container( - constraints: BoxConstraints.loose(Size.fromHeight(400)), - decoration: BoxDecoration( - color: StreamChatTheme.of(context).primaryColor, - borderRadius: BorderRadius.circular(8.0)), - child: ListView( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - children: [ - if (commands.isNotEmpty) - Padding( - padding: const EdgeInsets.only(left: 8.0, top: 8.0), - child: Row( + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutExpo, + builder: (context, val, wid) { + return Transform.scale( + alignment: Alignment.center, + scale: val, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Card( + elevation: 2.0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + color: StreamChatTheme.of(context).primaryColor, + clipBehavior: Clip.antiAlias, + child: Container( + constraints: BoxConstraints.loose(Size.fromHeight(400)), + decoration: BoxDecoration( + color: StreamChatTheme.of(context).primaryColor, + borderRadius: BorderRadius.circular(8.0)), + child: ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, children: [ - Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0), - child: Icon( - StreamIcons.lightning, - color: StreamChatTheme.of(context).accentColor, + if (commands.isNotEmpty) + Padding( + padding: + const EdgeInsets.only(left: 8.0, top: 8.0), + child: Row( + children: [ + Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0), + child: Icon( + StreamIcons.lightning, + color: StreamChatTheme.of(context) + .accentColor, + ), + ), + Text( + 'Instant Commands', + style: TextStyle( + color: Colors.black.withOpacity(.5), + ), + ) + ], + ), ), - ), - Text( - 'Instant Commands', - style: TextStyle( - color: Colors.black.withOpacity(.5), - ), - ) + ...commands + .map( + (c) => ListTile( + leading: c.name == 'giphy' + ? _buildGiphyIcon() + : null, + title: Text.rich( + TextSpan( + text: '${c.name.capitalize()}', + style: TextStyle( + fontWeight: FontWeight.bold), + children: [ + TextSpan( + text: ' /${c.name} ${c.args}', + style: TextStyle( + fontWeight: FontWeight.w300, + ), + ), + ], + ), + ), + trailing: CircleAvatar( + backgroundColor: + StreamChatTheme.of(context).accentColor, + child: Icon( + StreamIcons.lightning, + color: Colors.white, + size: 12.5, + ), + maxRadius: 12, + ), + //subtitle: Text(c.description), + onTap: () { + _setCommand(c); + }, + ), + ) + .toList(), ], ), ), - ...commands - .map( - (c) => ListTile( - leading: c.name == 'giphy' ? _buildGiphyIcon() : null, - title: Text.rich( - TextSpan( - text: '${c.name.capitalize()}', - style: TextStyle(fontWeight: FontWeight.bold), - children: [ - TextSpan( - text: ' /${c.name} ${c.args}', - style: TextStyle( - fontWeight: FontWeight.w300, - ), - ), - ], - ), - ), - trailing: CircleAvatar( - backgroundColor: - StreamChatTheme.of(context).accentColor, - child: Icon( - StreamIcons.lightning, - color: Colors.white, - size: 12.5, - ), - maxRadius: 12, - ), - //subtitle: Text(c.description), - onTap: () { - _setCommand(c); - }, - ), - ) - .toList(), - ], - ), - ), - ), - ), + ), + ), + ); + }), ); }); } @@ -925,71 +940,84 @@ class MessageInputState extends State { bottom: size.height + MediaQuery.of(context).viewInsets.bottom, left: 0, right: 0, - child: Card( - margin: EdgeInsets.all(8.0), - elevation: 2.0, - color: StreamChatTheme.of(context).primaryColor, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - ), - clipBehavior: Clip.antiAlias, - child: Container( - constraints: BoxConstraints.loose(Size.fromHeight(400)), - decoration: BoxDecoration( - color: StreamChatTheme.of(context).primaryColor, - ), - child: FutureBuilder>( - future: queryMembers ?? Future.value(members), - initialData: members, - builder: (context, snapshot) { - return ListView( - padding: const EdgeInsets.all(0), - shrinkWrap: true, - children: snapshot.data - .map((m) => ListTile( - leading: UserAvatar( - constraints: BoxConstraints.tight( - Size( - 40, - 40, - ), - ), - user: m.user, - ), - title: Text( - '${m.user.name}', - style: TextStyle(fontWeight: FontWeight.bold), - ), - subtitle: Text('@${m.userId}'), - trailing: Icon( - StreamIcons.at_mention, - color: StreamChatTheme.of(context).accentColor, - ), - onTap: () { - _mentionedUsers.add(m.user); + child: TweenAnimationBuilder( + tween: Tween(begin: 0.0, end: 1.0), + duration: Duration(milliseconds: 300), + curve: Curves.easeInOutExpo, + builder: (context, val, wid) { + return Transform.scale( + scale: val, + child: Card( + margin: EdgeInsets.all(8.0), + elevation: 2.0, + color: StreamChatTheme.of(context).primaryColor, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(8.0), + ), + clipBehavior: Clip.antiAlias, + child: Container( + constraints: BoxConstraints.loose(Size.fromHeight(400)), + decoration: BoxDecoration( + color: StreamChatTheme.of(context).primaryColor, + ), + child: FutureBuilder>( + future: queryMembers ?? Future.value(members), + initialData: members, + builder: (context, snapshot) { + return ListView( + padding: const EdgeInsets.all(0), + shrinkWrap: true, + children: snapshot.data + .map((m) => ListTile( + leading: UserAvatar( + constraints: BoxConstraints.tight( + Size( + 40, + 40, + ), + ), + user: m.user, + ), + title: Text( + '${m.user.name}', + style: TextStyle( + fontWeight: FontWeight.bold), + ), + subtitle: Text('@${m.userId}'), + trailing: Icon( + StreamIcons.at_mention, + color: StreamChatTheme.of(context) + .accentColor, + ), + onTap: () { + _mentionedUsers.add(m.user); - splits[splits.length - 1] = m.user.name; - final rejoin = splits.join('@'); + splits[splits.length - 1] = m.user.name; + final rejoin = splits.join('@'); - textEditingController.value = TextEditingValue( - text: rejoin + - textEditingController.text.substring( - textEditingController - .selection.start), - selection: TextSelection.collapsed( - offset: rejoin.length, - ), - ); + textEditingController.value = + TextEditingValue( + text: rejoin + + textEditingController.text + .substring( + textEditingController + .selection.start), + selection: TextSelection.collapsed( + offset: rejoin.length, + ), + ); - _mentionsOverlay?.remove(); - _mentionsOverlay = null; - }, - )) - .toList(), - ); - }), - ), - ), + _mentionsOverlay?.remove(); + _mentionsOverlay = null; + }, + )) + .toList(), + ); + }), + ), + ), + ); + }), ); }); } From e952d350c54ebc9951cd3bc5b85adfdc8228455e Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 18:10:23 +0530 Subject: [PATCH 04/10] fix: Stop animation before pop --- lib/src/reaction_picker.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index 1946e9ff..d661621e 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -119,17 +119,24 @@ class _ReactionPickerState extends State { } } + void pop() async { + for (var a in animations) { + a.stop(); + } + Navigator.of(context).pop(); + } + /// Add a reaction to the message void sendReaction(BuildContext context, String reactionType) { StreamChannel.of(context) .channel .sendReaction(widget.message, reactionType); - Navigator.of(context).pop(); + pop(); } /// Remove a reaction from the message void removeReaction(BuildContext context, Reaction reaction) { StreamChannel.of(context).channel.deleteReaction(widget.message, reaction); - Navigator.of(context).pop(); + pop(); } } From 5b06e9c257ce5875fe40382b4343ef520346c4d9 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 18:16:03 +0530 Subject: [PATCH 05/10] fix: Stop animation before pop --- lib/src/reaction_picker.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index d661621e..802e8eb3 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -122,6 +122,7 @@ class _ReactionPickerState extends State { void pop() async { for (var a in animations) { a.stop(); + a.dispose(); } Navigator.of(context).pop(); } From ee5cec3c4566cdde3c46125fd6bafc0dc1bbb4e5 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 18:27:12 +0530 Subject: [PATCH 06/10] fix: Stop animation before pop --- lib/src/reaction_picker.dart | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index 802e8eb3..71ddcbb5 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -25,7 +25,8 @@ class ReactionPicker extends StatefulWidget { _ReactionPickerState createState() => _ReactionPickerState(); } -class _ReactionPickerState extends State { +class _ReactionPickerState extends State + with TickerProviderStateMixin { List animations = []; @override @@ -35,10 +36,14 @@ class _ReactionPickerState extends State { 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)), + EzAnimation.sequence( + [ + SequenceItem(0.0, 1.4), + SequenceItem(1.4, 1.0), + ], + Duration(milliseconds: 500), + vsync: this, + ), ); }); From b0b05e165cfa18591673ad57cabfb0730f38d308 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Fri, 20 Nov 2020 18:30:50 +0530 Subject: [PATCH 07/10] fix: Stop animation before pop --- lib/src/reaction_picker.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index 71ddcbb5..8d337498 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -127,7 +127,7 @@ class _ReactionPickerState extends State void pop() async { for (var a in animations) { a.stop(); - a.dispose(); + //a.dispose(); } Navigator.of(context).pop(); } From 0ba45d82beb88ca2e49a9b0534e8522806096299 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 23 Nov 2020 15:46:38 +0530 Subject: [PATCH 08/10] fix: Fixed dispose errors --- lib/src/reaction_picker.dart | 9 ++++++++- pubspec.yaml | 2 +- test/src/message_action_modal_test.dart | 1 + test/src/message_reaction_modal_test.dart | 1 + 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/src/reaction_picker.dart b/lib/src/reaction_picker.dart index 8d337498..83698e46 100644 --- a/lib/src/reaction_picker.dart +++ b/lib/src/reaction_picker.dart @@ -127,7 +127,6 @@ class _ReactionPickerState extends State void pop() async { for (var a in animations) { a.stop(); - //a.dispose(); } Navigator.of(context).pop(); } @@ -145,4 +144,12 @@ class _ReactionPickerState extends State StreamChannel.of(context).channel.deleteReaction(widget.message, reaction); pop(); } + + @override + void dispose() { + for (var a in animations) { + a?.dispose(); + } + super.dispose(); + } } diff --git a/pubspec.yaml b/pubspec.yaml index 689a54b8..85f164b8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -38,7 +38,7 @@ dependencies: media_gallery: ^0.1.5 permission_handler: ^5.0.1+1 transparent_image: ^1.0.0 - ezanimation: ^0.4.0 + ezanimation: ^0.4.1 flutter: assets: diff --git a/test/src/message_action_modal_test.dart b/test/src/message_action_modal_test.dart index 1427edd6..b186ab91 100644 --- a/test/src/message_action_modal_test.dart +++ b/test/src/message_action_modal_test.dart @@ -40,6 +40,7 @@ void main() { ); await tester.pump(); + await tester.pump(Duration(milliseconds: 1000)); expect(find.byKey(Key('MessageWidget')), findsOneWidget); expect(find.byIcon(StreamIcons.sorting_up), findsOneWidget); expect(find.byIcon(StreamIcons.edit), findsOneWidget); diff --git a/test/src/message_reaction_modal_test.dart b/test/src/message_reaction_modal_test.dart index e270793e..4cf3dafa 100644 --- a/test/src/message_reaction_modal_test.dart +++ b/test/src/message_reaction_modal_test.dart @@ -87,6 +87,7 @@ void main() { ); await tester.pump(); + await tester.pump(Duration(milliseconds: 1000)); expect(find.byKey(Key('MessageWidget')), findsOneWidget); expect(find.byIcon(StreamIcons.thumbs_up_reaction), findsNWidgets(2)); expect(find.byIcon(StreamIcons.love_reaction), findsNWidgets(2)); From cb4bd6849d0dfc9ece7c2d5982bb247ac2c1b308 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 23 Nov 2020 16:05:16 +0530 Subject: [PATCH 09/10] fmt --- lib/src/message_actions_modal.dart | 129 +++++++++++++++-------------- 1 file changed, 66 insertions(+), 63 deletions(-) diff --git a/lib/src/message_actions_modal.dart b/lib/src/message_actions_modal.dart index 049f1be7..8b53a973 100644 --- a/lib/src/message_actions_modal.dart +++ b/lib/src/message_actions_modal.dart @@ -96,78 +96,81 @@ class MessageActionsModal extends StatelessWidget { ), ), TweenAnimationBuilder( - 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( - key: Key('MessageWidget'), - reverse: reverse, - message: message.copyWith( - text: message.text.length > 200 - ? '${message.text.substring(0, 200)}...' - : message.text, + 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( + key: Key('MessageWidget'), + reverse: reverse, + message: message.copyWith( + text: message.text.length > 200 + ? '${message.text.substring(0, 200)}...' + : message.text, + ), + messageTheme: messageTheme, + showReactions: false, + showUsername: false, + showReplyIndicator: false, + showUserAvatar: showUserAvatar, + showTimestamp: false, + translateUserAvatar: false, + showReactionPickerIndicator: true, + showSendingIndicator: DisplayWidget.gone, + shape: messageShape, ), - messageTheme: messageTheme, - showReactions: false, - showUsername: false, - showReplyIndicator: false, - showUserAvatar: showUserAvatar, - showTimestamp: false, - translateUserAvatar: false, - showReactionPickerIndicator: true, - showSendingIndicator: DisplayWidget.gone, - shape: messageShape, ), - ), - ); - } - ), + ); + }), SizedBox( height: 8, ), TweenAnimationBuilder( - 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( - horizontal: 48.0, - ), - child: Material( - clipBehavior: Clip.hardEdge, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + 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( + horizontal: 48.0, ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: ListTile.divideTiles( - context: context, - tiles: [ - if (showReply && - (message.status == - MessageSendingStatus.SENT || - message.status == null) && - message.parentId == null) - _buildReplyButton(context), - if (showEditMessage) _buildEditMessage(context), - if (showDeleteMessage) - _buildDeleteButton(context), - if (showCopyMessage) _buildCopyButton(context), - ], - ).toList(), + child: Material( + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + child: Column( + crossAxisAlignment: + CrossAxisAlignment.stretch, + children: ListTile.divideTiles( + context: context, + tiles: [ + if (showReply && + (message.status == + MessageSendingStatus.SENT || + message.status == null) && + message.parentId == null) + _buildReplyButton(context), + if (showEditMessage) + _buildEditMessage(context), + if (showDeleteMessage) + _buildDeleteButton(context), + if (showCopyMessage) + _buildCopyButton(context), + ], + ).toList(), + ), ), ), - ), - ); - } - ) + ); + }) ], ), ), From 162971636e5be112929cf4c3bc73310879e08af8 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 23 Nov 2020 16:13:13 +0530 Subject: [PATCH 10/10] fix: Fixed tests --- test/src/message_action_modal_test.dart | 1 + test/src/message_reaction_modal_test.dart | 2 ++ 2 files changed, 3 insertions(+) diff --git a/test/src/message_action_modal_test.dart b/test/src/message_action_modal_test.dart index b186ab91..97709e70 100644 --- a/test/src/message_action_modal_test.dart +++ b/test/src/message_action_modal_test.dart @@ -85,6 +85,7 @@ void main() { ); await tester.pump(); + await tester.pump(Duration(milliseconds: 1000)); expect(find.byKey(Key('MessageWidget')), findsOneWidget); expect(find.byIcon(StreamIcons.sorting_up), findsNothing); expect(find.byIcon(StreamIcons.edit), findsNothing); diff --git a/test/src/message_reaction_modal_test.dart b/test/src/message_reaction_modal_test.dart index 4cf3dafa..622b57c3 100644 --- a/test/src/message_reaction_modal_test.dart +++ b/test/src/message_reaction_modal_test.dart @@ -39,6 +39,8 @@ void main() { ), ); + await tester.pump(Duration(milliseconds: 1000)); + expect(find.byKey(Key('MessageWidget')), findsOneWidget); expect(find.byIcon(StreamIcons.thumbs_up_reaction), findsOneWidget); },