diff --git a/lib/src/views/chats/chat_messages.view.dart b/lib/src/views/chats/chat_messages.view.dart index 4ba73e2..273aca8 100644 --- a/lib/src/views/chats/chat_messages.view.dart +++ b/lib/src/views/chats/chat_messages.view.dart @@ -18,6 +18,7 @@ import 'package:twonly/src/views/chats/chat_messages_components/entries/chat_dat import 'package:twonly/src/views/chats/chat_messages_components/message_input.dart'; import 'package:twonly/src/views/chats/chat_messages_components/response_container.dart'; import 'package:twonly/src/views/components/avatar_icon.component.dart'; +import 'package:twonly/src/views/components/blink.component.dart'; import 'package:twonly/src/views/components/flame.dart'; import 'package:twonly/src/views/components/verified_shield.dart'; import 'package:twonly/src/views/contact/contact.view.dart'; @@ -372,36 +373,25 @@ class _ChatMessagesViewState extends State { ); } else { final chatMessage = messages[i].message!; - return Transform.translate( - offset: Offset( - (focusedScrollItem == i) - ? (chatMessage.senderId == null) - ? -8 - : 8 - : 0, - 0, - ), - child: Transform.scale( - scale: (focusedScrollItem == i) ? 1.05 : 1, - child: ChatListEntry( - key: Key(chatMessage.messageId), - message: messages[i].message!, - nextMessage: - (i > 0) ? messages[i - 1].message : null, - prevMessage: ((i + 1) < messages.length) - ? messages[i + 1].message - : null, - group: group, - galleryItems: galleryItems, - userIdToContact: userIdToContact, - scrollToMessage: scrollToMessage, - onResponseTriggered: () { - setState(() { - quotesMessage = chatMessage; - }); - textFieldFocus.requestFocus(); - }, - ), + return BlinkWidget( + enabled: focusedScrollItem == i, + child: ChatListEntry( + key: Key(chatMessage.messageId), + message: messages[i].message!, + nextMessage: (i > 0) ? messages[i - 1].message : null, + prevMessage: ((i + 1) < messages.length) + ? messages[i + 1].message + : null, + group: group, + galleryItems: galleryItems, + userIdToContact: userIdToContact, + scrollToMessage: scrollToMessage, + onResponseTriggered: () { + setState(() { + quotesMessage = chatMessage; + }); + textFieldFocus.requestFocus(); + }, ), ); } diff --git a/lib/src/views/components/blink.component.dart b/lib/src/views/components/blink.component.dart new file mode 100644 index 0000000..3a9c094 --- /dev/null +++ b/lib/src/views/components/blink.component.dart @@ -0,0 +1,75 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +class BlinkWidget extends StatefulWidget { + const BlinkWidget({ + required this.child, + required this.enabled, + super.key, + this.blinkDuration = const Duration(milliseconds: 2500), + this.interval = const Duration(milliseconds: 250), + this.visibleOpacity = 1.0, + this.hiddenOpacity = 0.4, + }); + final bool enabled; + final Widget child; + final Duration blinkDuration; + final Duration interval; + final double visibleOpacity; + final double hiddenOpacity; + + @override + State createState() => _BlinkWidgetState(); +} + +class _BlinkWidgetState extends State + with SingleTickerProviderStateMixin { + late Ticker _ticker; + bool _visible = true; + + @override + void initState() { + super.initState(); + _ticker = createTicker(_onTick); + } + + @override + void didUpdateWidget(covariant BlinkWidget oldWidget) { + if (oldWidget.enabled != widget.enabled) { + if (widget.enabled) { + _ticker + ..stop() + ..start(); + } + } + super.didUpdateWidget(oldWidget); + } + + void _onTick(Duration elapsed) { + var visible = true; + if (elapsed.inMilliseconds < widget.blinkDuration.inMilliseconds) { + visible = elapsed.inMilliseconds % (widget.interval.inMilliseconds * 2) < + widget.interval.inMilliseconds; + } else { + _ticker.stop(); + } + setState(() => _visible = visible); + } + + @override + void dispose() { + _ticker.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedOpacity( + opacity: _visible ? widget.visibleOpacity : widget.hiddenOpacity, + duration: Duration( + milliseconds: widget.blinkDuration.inMilliseconds ~/ 3, + ), + child: widget.child, + ); + } +}