diff --git a/assets/typing.gif b/assets/typing.gif deleted file mode 100644 index 7c8154721..000000000 Binary files a/assets/typing.gif and /dev/null differ diff --git a/assets/typing.svg b/assets/typing.svg deleted file mode 100644 index a12646465..000000000 --- a/assets/typing.svg +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - diff --git a/lib/pages/chat/typing_indicators.dart b/lib/pages/chat/typing_indicators.dart index b48f5f903..e816f465d 100644 --- a/lib/pages/chat/typing_indicators.dart +++ b/lib/pages/chat/typing_indicators.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:fluffychat/config/app_config.dart'; @@ -78,14 +80,8 @@ class TypingIndicators extends StatelessWidget { bottomRight: Radius.circular(AppConfig.borderRadius), ), child: Padding( - padding: const EdgeInsets.all(8), - child: typingUsers.isEmpty - ? null - : Image.asset( - 'assets/typing.gif', - height: 30, - filterQuality: FilterQuality.high, - ), + padding: const EdgeInsets.symmetric(horizontal: 8), + child: typingUsers.isEmpty ? null : const _TypingDots(), ), ), ), @@ -95,3 +91,66 @@ class TypingIndicators extends StatelessWidget { ); } } + +class _TypingDots extends StatefulWidget { + const _TypingDots(); + + @override + State<_TypingDots> createState() => __TypingDotsState(); +} + +class __TypingDotsState extends State<_TypingDots> { + int _tick = 0; + + late final Timer _timer; + + static const Duration animationDuration = Duration(milliseconds: 300); + + @override + void initState() { + _timer = Timer.periodic( + animationDuration, + (_) { + if (!mounted) { + return; + } + setState(() { + _tick = (_tick + 1) % 4; + }); + }, + ); + super.initState(); + } + + @override + void dispose() { + _timer.cancel(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + const size = 8.0; + + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + for (var i = 1; i <= 3; i++) + AnimatedContainer( + duration: animationDuration * 1.5, + curve: FluffyThemes.animationCurve, + width: size, + height: _tick == i ? size * 2 : size, + margin: EdgeInsets.symmetric( + horizontal: 2, + vertical: _tick == i ? 4 : 8, + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(size * 2), + color: Theme.of(context).colorScheme.secondary, + ), + ), + ], + ); + } +}