Repository: aagarwal1012/Animated-Text-Kit

Issue:
Animation control with `AnimationController`
**Is your feature request related to a problem? Please describe.**
I want to control the animation with `AnimationController`

**Describe the solution you'd like**
I want to get `AnimationController` in the `AnimatedTextKit` constructor so that the animation can be managed.

**Describe alternatives you've considered**
n/a

**Additional context**
n/a

Controller Request 
Hello devs i am really impressed of  the animation you had done and it is very good but will you upgrade to give a controller to make animation finish on tap or controller to stop animation in half or when animation listener this will be all good if you will add controller . and onFinished function is not working  .




thank you 




Fix patch:
diff --git a/README.md b/README.md
index b18288a..b2fe4e8 100644
--- a/README.md
+++ b/README.md
@@ -119,11 +119,12 @@ AnimatedTextKit(
       speed: const Duration(milliseconds: 2000),
     ),
   ],
-  
+
   totalRepeatCount: 4,
   pause: const Duration(milliseconds: 1000),
   displayFullTextOnTap: true,
   stopPauseOnTap: true,
+  controller: myAnimatedTextController
 )
 ```
 
@@ -134,6 +135,7 @@ It has many configurable properties, including:
 - `isRepeatingAnimation` – controls whether the animation repeats
 - `repeatForever` – controls whether the animation repeats forever
 - `totalRepeatCount` – number of times the animation should repeat (when `repeatForever` is `false`)
+- `controller` - It allows for control over the animation by providing methods to play, pause and reset the text animations programmatically
 
 There are also custom callbacks:
 
diff --git a/example/lib/main.dart b/example/lib/main.dart
index c5c618b..fa83ee1 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -33,6 +33,8 @@ class _MyHomePageState extends State<MyHomePage> {
   int _index = 0;
   int _tapCount = 0;
 
+  bool _isAnimationPaused = false;
+
   @override
   void initState() {
     super.initState();
@@ -77,18 +79,58 @@ class _MyHomePageState extends State<MyHomePage> {
           ),
         ],
       ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: () {
-          setState(() {
-            _index = ++_index % _examples.length;
-            _tapCount = 0;
-          });
-        },
-        tooltip: 'Next',
-        child: const Icon(
-          Icons.play_circle_filled,
-          size: 50.0,
-        ),
+      floatingActionButton: Row(
+        mainAxisAlignment: MainAxisAlignment.end,
+        children: [
+          FloatingActionButton(
+            onPressed: () {
+              animatedTextExample.controller.reset();
+              setState(() {
+                _isAnimationPaused = false;
+                _tapCount = 0;
+              });
+            },
+            tooltip: 'Reset current animation',
+            child: const Icon(
+              Icons.replay_sharp,
+              size: 50.0,
+            ),
+          ),
+          const SizedBox(width: 16),
+          FloatingActionButton(
+              onPressed: () {
+                if (_isAnimationPaused) {
+                  animatedTextExample.controller.play();
+                  setState(() {
+                    _isAnimationPaused = false;
+                  });
+                } else {
+                  animatedTextExample.controller.pause();
+                  setState(() {
+                    _isAnimationPaused = true;
+                  });
+                }
+              },
+              tooltip: _isAnimationPaused ? 'Play' : 'Pause',
+              child: Icon(
+                _isAnimationPaused ? Icons.play_circle : Icons.pause_circle,
+                size: 50,
+              )),
+          const SizedBox(width: 16),
+          FloatingActionButton(
+            onPressed: () {
+              setState(() {
+                _index = ++_index % _examples.length;
+                _tapCount = 0;
+              });
+            },
+            tooltip: 'Next',
+            child: const Icon(
+              Icons.arrow_right,
+              size: 50.0,
+            ),
+          ),
+        ],
       ),
     );
   }
@@ -98,12 +140,13 @@ class AnimatedTextExample {
   final String label;
   final Color? color;
   final Widget child;
+  final AnimatedTextController controller;
 
-  const AnimatedTextExample({
-    required this.label,
-    required this.color,
-    required this.child,
-  });
+  const AnimatedTextExample(
+      {required this.label,
+      required this.color,
+      required this.child,
+      required this.controller});
 }
 
 // Colorize Text Style
@@ -120,259 +163,290 @@ const _colorizeColors = [
   Colors.red,
 ];
 
-List<AnimatedTextExample> animatedTextExamples({VoidCallback? onTap}) =>
-    <AnimatedTextExample>[
-      AnimatedTextExample(
-        label: 'Rotate',
-        color: Colors.orange[800],
-        child: ListView(
-          scrollDirection: Axis.horizontal,
-          children: <Widget>[
-            Row(
-              mainAxisSize: MainAxisSize.min,
-              children: <Widget>[
-                const SizedBox(
-                  width: 20.0,
-                  height: 100.0,
-                ),
-                const Text(
-                  'Be',
-                  style: TextStyle(fontSize: 43.0),
-                ),
-                const SizedBox(
-                  width: 20.0,
-                  height: 100.0,
+List<AnimatedTextExample> animatedTextExamples({VoidCallback? onTap}) {
+  final rotateController = AnimatedTextController();
+  final fadeController = AnimatedTextController();
+  final typerController = AnimatedTextController();
+  final typewriterController = AnimatedTextController();
+  final scaleController = AnimatedTextController();
+  final colorizeController = AnimatedTextController();
+  final textLiquidFillController = AnimatedTextController();
+  final wavyTextController = AnimatedTextController();
+  final flickerController = AnimatedTextController();
+  final combinationController = AnimatedTextController();
+
+  return <AnimatedTextExample>[
+    AnimatedTextExample(
+      label: 'Rotate',
+      color: Colors.orange[800],
+      controller: rotateController,
+      child: ListView(
+        scrollDirection: Axis.horizontal,
+        children: <Widget>[
+          Row(
+            mainAxisSize: MainAxisSize.min,
+            children: <Widget>[
+              const SizedBox(
+                width: 20.0,
+                height: 100.0,
+              ),
+              const Text(
+                'Be',
+                style: TextStyle(fontSize: 43.0),
+              ),
+              const SizedBox(
+                width: 20.0,
+                height: 100.0,
+              ),
+              DefaultTextStyle(
+                style: TextStyle(
+                  fontSize: 40.0,
+                  fontFamily: 'Horizon',
                 ),
-                DefaultTextStyle(
-                  style: TextStyle(
-                    fontSize: 40.0,
-                    fontFamily: 'Horizon',
-                  ),
-                  child: AnimatedTextKit(
-                    animatedTexts: [
-                      RotateAnimatedText('AWESOME'),
-                      RotateAnimatedText('OPTIMISTIC'),
-                      RotateAnimatedText(
-                        'DIFFERENT',
-                        textStyle: const TextStyle(
-                          decoration: TextDecoration.underline,
-                        ),
+                child: AnimatedTextKit(
+                  animatedTexts: [
+                    RotateAnimatedText('AWESOME'),
+                    RotateAnimatedText('OPTIMISTIC'),
+                    RotateAnimatedText(
+                      'DIFFERENT',
+                      textStyle: const TextStyle(
+                        decoration: TextDecoration.underline,
                       ),
-                    ],
-                    onTap: onTap,
-                    isRepeatingAnimation: true,
-                    totalRepeatCount: 10,
-                  ),
+                    ),
+                  ],
+                  controller: rotateController,
+                  onTap: onTap,
+                  isRepeatingAnimation: true,
+                  totalRepeatCount: 10,
                 ),
-              ],
-            ),
+              ),
+            ],
+          ),
+        ],
+      ),
+    ),
+    AnimatedTextExample(
+      label: 'Fade',
+      color: Colors.brown[600],
+      controller: fadeController,
+      child: DefaultTextStyle(
+        style: const TextStyle(
+          fontSize: 32.0,
+          fontWeight: FontWeight.bold,
+        ),
+        child: AnimatedTextKit(
+          animatedTexts: [
+            FadeAnimatedText('do IT!'),
+            FadeAnimatedText('do it RIGHT!!'),
+            FadeAnimatedText('do it RIGHT NOW!!!'),
           ],
+          controller: fadeController,
+          onTap: onTap,
         ),
       ),
-      AnimatedTextExample(
-        label: 'Fade',
-        color: Colors.brown[600],
+    ),
+    AnimatedTextExample(
+      label: 'Typer',
+      color: Colors.lightGreen[800],
+      controller: typerController,
+      child: SizedBox(
+        width: 250.0,
         child: DefaultTextStyle(
           style: const TextStyle(
-            fontSize: 32.0,
-            fontWeight: FontWeight.bold,
+            fontSize: 30.0,
+            fontFamily: 'Bobbers',
           ),
           child: AnimatedTextKit(
             animatedTexts: [
-              FadeAnimatedText('do IT!'),
-              FadeAnimatedText('do it RIGHT!!'),
-              FadeAnimatedText('do it RIGHT NOW!!!'),
+              TyperAnimatedText('It is not enough to do your best,'),
+              TyperAnimatedText('you must know what to do,'),
+              TyperAnimatedText('and then do your best'),
+              TyperAnimatedText('- W.Edwards Deming'),
             ],
+            controller: typerController,
             onTap: onTap,
           ),
         ),
       ),
-      AnimatedTextExample(
-        label: 'Typer',
-        color: Colors.lightGreen[800],
-        child: SizedBox(
-          width: 250.0,
-          child: DefaultTextStyle(
-            style: const TextStyle(
-              fontSize: 30.0,
-              fontFamily: 'Bobbers',
-            ),
-            child: AnimatedTextKit(
-              animatedTexts: [
-                TyperAnimatedText('It is not enough to do your best,'),
-                TyperAnimatedText('you must know what to do,'),
-                TyperAnimatedText('and then do your best'),
-                TyperAnimatedText('- W.Edwards Deming'),
-              ],
-              onTap: onTap,
-            ),
-          ),
-        ),
-      ),
-      AnimatedTextExample(
-        label: 'Typewriter',
-        color: Colors.teal[700],
-        child: SizedBox(
-          width: 250.0,
-          child: DefaultTextStyle(
-            style: const TextStyle(
-              fontSize: 30.0,
-              fontFamily: 'Agne',
-            ),
-            child: AnimatedTextKit(
-              animatedTexts: [
-                TypewriterAnimatedText('Discipline is the best tool'),
-                TypewriterAnimatedText('Design first, then code', cursor: '|'),
-                TypewriterAnimatedText('Do not patch bugs out, rewrite them',
-                    cursor: '<|>'),
-                TypewriterAnimatedText('Do not test bugs out, design them out',
-                    cursor: '💡'),
-              ],
-              onTap: onTap,
-            ),
-          ),
-        ),
-      ),
-      AnimatedTextExample(
-        label: 'Scale',
-        color: Colors.blue[700],
+    ),
+    AnimatedTextExample(
+      label: 'Typewriter',
+      color: Colors.teal[700],
+      controller: typewriterController,
+      child: SizedBox(
+        width: 250.0,
         child: DefaultTextStyle(
           style: const TextStyle(
-            fontSize: 70.0,
-            fontFamily: 'Canterbury',
+            fontSize: 30.0,
+            fontFamily: 'Agne',
           ),
           child: AnimatedTextKit(
             animatedTexts: [
-              ScaleAnimatedText('Think'),
-              ScaleAnimatedText('Build'),
-              ScaleAnimatedText('Ship'),
+              TypewriterAnimatedText('Discipline is the best tool'),
+              TypewriterAnimatedText('Design first, then code', cursor: '|'),
+              TypewriterAnimatedText('Do not patch bugs out, rewrite them',
+                  cursor: '<|>'),
+              TypewriterAnimatedText('Do not test bugs out, design them out',
+                  cursor: '💡'),
             ],
+            controller: typewriterController,
             onTap: onTap,
           ),
         ),
       ),
-      AnimatedTextExample(
-        label: 'Colorize',
-        color: Colors.blueGrey[50],
+    ),
+    AnimatedTextExample(
+      label: 'Scale',
+      color: Colors.blue[700],
+      controller: scaleController,
+      child: DefaultTextStyle(
+        style: const TextStyle(
+          fontSize: 70.0,
+          fontFamily: 'Canterbury',
+        ),
         child: AnimatedTextKit(
           animatedTexts: [
-            ColorizeAnimatedText(
-              'Larry Page',
-              textStyle: _colorizeTextStyle,
-              colors: _colorizeColors,
-            ),
-            ColorizeAnimatedText(
-              'Bill Gates',
-              textStyle: _colorizeTextStyle,
-              colors: _colorizeColors,
-            ),
-            ColorizeAnimatedText(
-              'Steve Jobs',
-              textStyle: _colorizeTextStyle,
-              colors: _colorizeColors,
-            ),
+            ScaleAnimatedText('Think'),
+            ScaleAnimatedText('Build'),
+            ScaleAnimatedText('Ship'),
           ],
+          controller: scaleController,
           onTap: onTap,
         ),
       ),
-      AnimatedTextExample(
-        label: 'TextLiquidFill',
-        color: Colors.white,
-        child: TextLiquidFill(
-          text: 'LIQUIDY',
-          waveColor: Colors.blueAccent,
-          boxBackgroundColor: Colors.redAccent,
-          textStyle: const TextStyle(
-            fontSize: 70,
-            fontWeight: FontWeight.bold,
+    ),
+    AnimatedTextExample(
+      label: 'Colorize',
+      color: Colors.blueGrey[50],
+      controller: colorizeController,
+      child: AnimatedTextKit(
+        animatedTexts: [
+          ColorizeAnimatedText(
+            'Larry Page',
+            textStyle: _colorizeTextStyle,
+            colors: _colorizeColors,
           ),
-          boxHeight: 300,
-        ),
-      ),
-      AnimatedTextExample(
-        label: 'Wavy Text',
-        color: Colors.purple,
-        child: DefaultTextStyle(
-          style: const TextStyle(
-            fontSize: 20.0,
+          ColorizeAnimatedText(
+            'Bill Gates',
+            textStyle: _colorizeTextStyle,
+            colors: _colorizeColors,
           ),
-          child: AnimatedTextKit(
-            animatedTexts: [
-              WavyAnimatedText(
-                'Hello World',
-                textStyle: const TextStyle(
-                  fontSize: 24.0,
-                  fontWeight: FontWeight.bold,
-                ),
-              ),
-              WavyAnimatedText('Look at the waves'),
-              WavyAnimatedText('They look so Amazing'),
-            ],
-            onTap: onTap,
+          ColorizeAnimatedText(
+            'Steve Jobs',
+            textStyle: _colorizeTextStyle,
+            colors: _colorizeColors,
           ),
-        ),
+        ],
+        controller: colorizeController,
+        onTap: onTap,
       ),
-      AnimatedTextExample(
-        label: 'Flicker',
-        color: Colors.pink[300],
-        child: DefaultTextStyle(
-          style: const TextStyle(
-            fontSize: 35,
-            color: Colors.white,
-            shadows: [
-              Shadow(
-                blurRadius: 7.0,
-                color: Colors.white,
-                offset: Offset(0, 0),
-              ),
-            ],
-          ),
-          child: AnimatedTextKit(
-            repeatForever: true,
-            animatedTexts: [
-              FlickerAnimatedText('Flicker Frenzy'),
-              FlickerAnimatedText('Night Vibes On'),
-              FlickerAnimatedText("C'est La Vie !"),
-            ],
-            onTap: onTap,
-          ),
+    ),
+    AnimatedTextExample(
+      label: 'TextLiquidFill',
+      color: Colors.white,
+      controller: textLiquidFillController,
+      child: TextLiquidFill(
+        text: 'LIQUIDY',
+        waveColor: Colors.blueAccent,
+        boxBackgroundColor: Colors.redAccent,
+        textStyle: const TextStyle(
+          fontSize: 70,
+          fontWeight: FontWeight.bold,
         ),
+        boxHeight: 300,
       ),
-      AnimatedTextExample(
-        label: 'Combination',
-        color: Colors.pink,
+    ),
+    AnimatedTextExample(
+      label: 'Wavy Text',
+      color: Colors.purple,
+      controller: wavyTextController,
+      child: DefaultTextStyle(
+        style: const TextStyle(
+          fontSize: 20.0,
+        ),
         child: AnimatedTextKit(
-          onTap: onTap,
           animatedTexts: [
             WavyAnimatedText(
-              'On Your Marks',
+              'Hello World',
               textStyle: const TextStyle(
                 fontSize: 24.0,
-              ),
-            ),
-            FadeAnimatedText(
-              'Get Set',
-              textStyle: const TextStyle(
-                fontSize: 32.0,
                 fontWeight: FontWeight.bold,
               ),
             ),
-            ScaleAnimatedText(
-              'Ready',
-              textStyle: const TextStyle(
-                fontSize: 48.0,
-                fontWeight: FontWeight.bold,
-              ),
+            WavyAnimatedText('Look at the waves'),
+            WavyAnimatedText('They look so Amazing'),
+          ],
+          controller: wavyTextController,
+          onTap: onTap,
+        ),
+      ),
+    ),
+    AnimatedTextExample(
+      label: 'Flicker',
+      color: Colors.pink[300],
+      controller: flickerController,
+      child: DefaultTextStyle(
+        style: const TextStyle(
+          fontSize: 35,
+          color: Colors.white,
+          shadows: [
+            Shadow(
+              blurRadius: 7.0,
+              color: Colors.white,
+              offset: Offset(0, 0),
             ),
-            RotateAnimatedText(
-              'Go!',
-              textStyle: const TextStyle(
-                fontSize: 64.0,
-              ),
-              rotateOut: false,
-              duration: const Duration(milliseconds: 400),
-            )
           ],
         ),
+        child: AnimatedTextKit(
+          repeatForever: true,
+          animatedTexts: [
+            FlickerAnimatedText('Flicker Frenzy'),
+            FlickerAnimatedText('Night Vibes On'),
+            FlickerAnimatedText("C'est La Vie !"),
+          ],
+          onTap: onTap,
+          controller: flickerController,
+        ),
       ),
-    ];
+    ),
+    AnimatedTextExample(
+      label: 'Combination',
+      color: Colors.pink,
+      controller: combinationController,
+      child: AnimatedTextKit(
+        onTap: onTap,
+        controller: combinationController,
+        animatedTexts: [
+          WavyAnimatedText(
+            'On Your Marks',
+            textStyle: const TextStyle(
+              fontSize: 24.0,
+            ),
+          ),
+          FadeAnimatedText(
+            'Get Set',
+            textStyle: const TextStyle(
+              fontSize: 32.0,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+          ScaleAnimatedText(
+            'Ready',
+            textStyle: const TextStyle(
+              fontSize: 48.0,
+              fontWeight: FontWeight.bold,
+            ),
+          ),
+          RotateAnimatedText(
+            'Go!',
+            textStyle: const TextStyle(
+              fontSize: 64.0,
+            ),
+            rotateOut: false,
+            duration: const Duration(milliseconds: 400),
+          )
+        ],
+      ),
+    ),
+  ];
+}
diff --git a/lib/animated_text_kit.dart b/lib/animated_text_kit.dart
index 632b22f..c10b450 100644
--- a/lib/animated_text_kit.dart
+++ b/lib/animated_text_kit.dart
@@ -1,6 +1,7 @@
 /// _Animated Text Kit_ is a library of some cool and awesome text animations.
 library animated_text_kit;
 
+export 'src/animated_text_controller.dart';
 export 'src/animated_text.dart';
 export 'src/typer.dart';
 export 'src/rotate.dart';
diff --git a/lib/src/animated_text.dart b/lib/src/animated_text.dart
index db3f851..887d8fe 100644
--- a/lib/src/animated_text.dart
+++ b/lib/src/animated_text.dart
@@ -1,5 +1,6 @@
 import 'dart:async';
 import 'dart:math';
+import 'package:animated_text_kit/src/animated_text_controller.dart';
 import 'package:flutter/material.dart';
 
 /// Abstract base class for text animations.
@@ -111,6 +112,14 @@ class AnimatedTextKit extends StatefulWidget {
   /// By default it is set to 3
   final int totalRepeatCount;
 
+  /// A controller for managing the state of an animated text sequence.
+  ///
+  /// This controller exposes methods to play, pause, and reset the animation.
+  /// The [AnimatedTextState] enum represents the various states the animation
+  /// can be in. By calling [play()], [pause()], or [reset()], you can transition
+  /// between these states and the animated widget will react accordingly.
+  final AnimatedTextController? controller;
+
   const AnimatedTextKit({
     Key? key,
     required this.animatedTexts,
@@ -121,6 +130,7 @@ class AnimatedTextKit extends StatefulWidget {
     this.onNext,
     this.onNextBeforePause,
     this.onFinished,
+    this.controller,
     this.isRepeatingAnimation = true,
     this.totalRepeatCount = 3,
     this.repeatForever = false,
@@ -140,24 +150,43 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
 
   late AnimatedText _currentAnimatedText;
 
+  late AnimatedTextController _animatedTextController;
+
   int _currentRepeatCount = 0;
 
   int _index = 0;
 
-  bool _isCurrentlyPausing = false;
-
   Timer? _timer;
 
   @override
   void initState() {
     super.initState();
+    _animatedTextController = widget.controller ?? AnimatedTextController();
+    _animatedTextController.stateNotifier.addListener(_stateChangedCallback);
     _initAnimation();
   }
 
+  void _stateChangedCallback() {
+    if (!mounted) return;
+    if (_animatedTextController.state == AnimatedTextState.playing &&
+        !_controller.isAnimating) {
+      _controller.forward();
+    } else if (_animatedTextController.state ==
+        AnimatedTextState.pausedByUser) {
+      _controller.stop();
+    } else if (_animatedTextController.state == AnimatedTextState.reset) {
+      _controller.reset();
+      _animatedTextController.state = AnimatedTextState.playing;
+    }
+  }
+
   @override
   void dispose() {
     _timer?.cancel();
     _controller.dispose();
+    _animatedTextController.stateNotifier.removeListener(_stateChangedCallback);
+    // Only dispose the controller if it was created by this widget
+    if (widget.controller == null) _animatedTextController.dispose();
     super.dispose();
   }
 
@@ -167,7 +196,9 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
     return GestureDetector(
       behavior: HitTestBehavior.opaque,
       onTap: _onTap,
-      child: _isCurrentlyPausing || !_controller.isAnimating
+      child: _animatedTextController.state ==
+                  AnimatedTextState.pausedBetweenAnimations ||
+              !_controller.isAnimating
           ? completeText
           : AnimatedBuilder(
               animation: _controller,
@@ -182,8 +213,6 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
   void _nextAnimation() {
     final isLast = _isLast;
 
-    _isCurrentlyPausing = false;
-
     // Handling onNext callback
     widget.onNext?.call(_index, isLast);
 
@@ -221,15 +250,25 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
 
     _currentAnimatedText.initAnimation(_controller);
 
-    _controller
-      ..addStatusListener(_animationEndCallback)
-      ..forward();
+    _controller.addStatusListener(_animationEndCallback);
+
+    if (_animatedTextController.state ==
+        AnimatedTextState.pausedBetweenAnimationsByUser) {
+      // This post frame callback is needed to ensure that the state is set and the widget is built
+      // before we pause the animation. otherwise nothing will be shown during the animation cycle
+      WidgetsBinding.instance.addPostFrameCallback((_) {
+        _animatedTextController.state = AnimatedTextState.pausedByUser;
+      });
+    }
+    _animatedTextController.state = AnimatedTextState.playing;
+    _controller.forward();
   }
 
-  void _setPause() {
+  void _setPauseBetweenAnimations() {
     final isLast = _isLast;
 
-    _isCurrentlyPausing = true;
+    _animatedTextController.state = AnimatedTextState.pausedBetweenAnimations;
+
     if (mounted) setState(() {});
 
     // Handle onNextBeforePause callback
@@ -238,7 +277,7 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
 
   void _animationEndCallback(AnimationStatus state) {
     if (state == AnimationStatus.completed) {
-      _setPause();
+      _setPauseBetweenAnimations();
       assert(null == _timer || !_timer!.isActive);
       _timer = Timer(widget.pause, _nextAnimation);
     }
@@ -246,7 +285,8 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
 
   void _onTap() {
     if (widget.displayFullTextOnTap) {
-      if (_isCurrentlyPausing) {
+      if (_animatedTextController.state ==
+          AnimatedTextState.pausedBetweenAnimations) {
         if (widget.stopPauseOnTap) {
           _timer?.cancel();
           _nextAnimation();
@@ -258,7 +298,7 @@ class _AnimatedTextKitState extends State<AnimatedTextKit>
 
         _controller.stop();
 
-        _setPause();
+        _setPauseBetweenAnimations();
 
         assert(null == _timer || !_timer!.isActive);
         _timer = Timer(
diff --git a/lib/src/animated_text_controller.dart b/lib/src/animated_text_controller.dart
new file mode 100644
index 0000000..3bac495
--- /dev/null
+++ b/lib/src/animated_text_controller.dart
@@ -0,0 +1,83 @@
+import 'package:flutter/material.dart';
+
+/// The various states that the animated text can be in:
+///
+/// * [playing]: The animation is currently running.
+/// * [userPaused]: The animation is paused due to a user action.
+/// * [pausingBetweenAnimations]: The animation has completed one segment and is
+///   currently in the built-in pause period before the next segment starts.
+/// * [pausingBetweenAnimationsWithUserPauseRequested]: The user requested a pause
+///   during the pause between animations, so once this pause period ends,
+///   the animation should remain paused.
+/// * [stopped]: The animation is stopped and will not progress further.
+/// * [reset]: The animation should reset to its initial state.
+enum AnimatedTextState {
+  playing,
+  pausedByUser,
+  pausedBetweenAnimations,
+  pausedBetweenAnimationsByUser,
+  stopped,
+  reset,
+}
+
+/// A controller for managing the state of an animated text sequence.
+///
+/// This controller exposes methods to play, pause, and reset the animation.
+/// The [AnimatedTextState] enum represents the various states the animation
+/// can be in. By calling [play()], [pause()], or [reset()], you can transition
+/// between these states and the animated widget will react accordingly.
+class AnimatedTextController {
+  /// A [ValueNotifier] that holds the current state of the animation.
+  /// Listeners can be attached to react when the state changes.
+  final ValueNotifier<AnimatedTextState> stateNotifier =
+      ValueNotifier<AnimatedTextState>(AnimatedTextState.playing);
+
+  /// Returns the current state of the animation.
+  AnimatedTextState get state => stateNotifier.value;
+
+  /// Sets the current state of the animation.
+  set state(AnimatedTextState state) {
+    stateNotifier.value = state;
+  }
+
+  /// Disposes of the [ValueNotifier]. This should be called when the
+  /// [AnimatedTextController] is no longer needed.
+  void dispose() {
+    stateNotifier.dispose();
+  }
+
+  /// Transitions the animation into the [playing] state, unless the controller is
+  /// currently in the [pausingBetweenAnimationsWithUserPauseRequested] state,
+  /// in which case it returns to the [pausingBetweenAnimations] state.
+  ///
+  /// Call this to resume the animation if it was previously paused.
+  void play() {
+    if (stateNotifier.value ==
+        AnimatedTextState.pausedBetweenAnimationsByUser) {
+      stateNotifier.value = AnimatedTextState.pausedBetweenAnimations;
+    } else {
+      stateNotifier.value = AnimatedTextState.playing;
+    }
+  }
+
+  /// Pauses the animation. If the animation is currently in the [pausingBetweenAnimations]
+  /// state, it moves to [pausingBetweenAnimationsWithUserPauseRequested], indicating
+  /// that once the internal pause finishes, the animation should remain paused.
+  /// Otherwise, it transitions directly into the [userPaused] state.
+  ///
+  /// Call this to pause the animation due to user interaction.
+  void pause() {
+    if (stateNotifier.value == AnimatedTextState.pausedBetweenAnimations) {
+      stateNotifier.value = AnimatedTextState.pausedBetweenAnimationsByUser;
+    } else {
+      stateNotifier.value = AnimatedTextState.pausedByUser;
+    }
+  }
+
+  /// Resets the animation to its initial state by setting the state to [reset].
+  /// This typically means the animated text should return to the start of its
+  /// animation in this cycle and be ready to begin again.
+  void reset() {
+    stateNotifier.value = AnimatedTextState.reset;
+  }
+}
diff --git a/lib/src/colorize.dart b/lib/src/colorize.dart
index f04f5b2..953d3f0 100644
--- a/lib/src/colorize.dart
+++ b/lib/src/colorize.dart
@@ -165,8 +165,8 @@ class ColorizeAnimatedTextKit extends AnimatedTextKit {
     TextDirection textDirection,
   ) =>
       text
-          .map((_) => ColorizeAnimatedText(
-                _,
+          .map((str) => ColorizeAnimatedText(
+                str,
                 textAlign: textAlign,
                 textStyle: textStyle,
                 speed: speed,
diff --git a/lib/src/fade.dart b/lib/src/fade.dart
index a6cdce8..e819890 100644
--- a/lib/src/fade.dart
+++ b/lib/src/fade.dart
@@ -36,7 +36,6 @@ class FadeAnimatedText extends AnimatedText {
         curve: Interval(0.0, fadeInEnd, curve: Curves.linear),
       ),
     );
-
     _fadeOut = Tween<double>(begin: 1.0, end: 0.0).animate(
       CurvedAnimation(
         parent: controller,
@@ -105,8 +104,8 @@ class FadeAnimatedTextKit extends AnimatedTextKit {
     double fadeOutBegin,
   ) =>
       text
-          .map((_) => FadeAnimatedText(
-                _,
+          .map((str) => FadeAnimatedText(
+                str,
                 textAlign: textAlign,
                 textStyle: textStyle,
                 duration: duration,
diff --git a/lib/src/wavy.dart b/lib/src/wavy.dart
index af79b00..6edbffb 100644
--- a/lib/src/wavy.dart
+++ b/lib/src/wavy.dart
@@ -35,21 +35,21 @@ class WavyAnimatedText extends AnimatedText {
   @override
   Widget animatedBuilder(BuildContext context, Widget? child) {
     final defaultTextStyle = DefaultTextStyle.of(context).style;
-    final scaleFactor = MediaQuery.of(context).textScaleFactor;
+    final textScaler = MediaQuery.textScalerOf(context);
     return RepaintBoundary(
       child: CustomPaint(
         painter: _WTextPainter(
           progress: _waveAnim.value,
           text: text,
           textStyle: defaultTextStyle.merge(textStyle),
-          scaleFactor: scaleFactor,
+          textScaler: textScaler,
         ),
         child: Text(
           text,
           style: defaultTextStyle
               .merge(textStyle)
               .merge(TextStyle(color: Colors.transparent)),
-          textScaleFactor: scaleFactor,
+          textScaler: textScaler,
         ),
       ),
     );
@@ -114,10 +114,11 @@ class _WTextPainter extends CustomPainter {
     required this.progress,
     required this.text,
     required this.textStyle,
-    required this.scaleFactor,
+    required this.textScaler,
   });
 
-  final double progress, scaleFactor;
+  final double progress;
+  final TextScaler textScaler;
   final String text;
   // Private class to store text information
   final _textLayoutInfo = <_TextLayoutInfo>[];
@@ -204,7 +205,7 @@ class _WTextPainter extends CustomPainter {
         style: textStyle,
       ),
       textDirection: TextDirection.ltr,
-      textScaleFactor: scaleFactor,
+      textScaler: textScaler,
     )..layout();
 
     textPainter.paint(
@@ -227,7 +228,7 @@ class _WTextPainter extends CustomPainter {
       ),
       textDirection: TextDirection.ltr,
       maxLines: 1,
-      textScaleFactor: scaleFactor,
+      textScaler: textScaler,
     );
 
     textPainter.layout();


Test patch:
diff --git a/test/controller_test.dart b/test/controller_test.dart
new file mode 100644
index 0000000..81fdcb1
--- /dev/null
+++ b/test/controller_test.dart
@@ -0,0 +1,56 @@
+import 'package:flutter_test/flutter_test.dart';
+import 'package:animated_text_kit/src/animated_text_controller.dart';
+
+void main() {
+  late AnimatedTextController controller;
+
+  setUp(() {
+    controller = AnimatedTextController();
+  });
+
+  tearDown(() {
+    controller.dispose();
+  });
+
+  test('Initial state should be playing', () {
+    expect(controller.state, AnimatedTextState.playing);
+  });
+
+  test('Calling pause when playing should set state to userPaused', () {
+    controller.pause();
+    expect(controller.state, AnimatedTextState.pausedByUser);
+  });
+
+  test('Calling play after paused should set state to playing', () {
+    controller.pause(); // userPaused
+    controller.play();
+    expect(controller.state, AnimatedTextState.playing);
+  });
+
+  test(
+      'Pausing during pausingBetweenAnimations should set state to pausingBetweenAnimationsWithUserPauseRequested',
+      () {
+    // Directly set state to pausingBetweenAnimations to simulate this scenario.
+    controller.state = AnimatedTextState.pausedBetweenAnimations;
+    controller.pause();
+    expect(controller.state, AnimatedTextState.pausedBetweenAnimationsByUser);
+  });
+
+  test(
+      'Calling play when in pausingBetweenAnimationsWithUserPauseRequested should revert to pausingBetweenAnimations',
+      () {
+    controller.state = AnimatedTextState.pausedBetweenAnimationsByUser;
+    controller.play();
+    expect(controller.state, AnimatedTextState.pausedBetweenAnimations);
+  });
+
+  test('Resetting should set state to reset', () {
+    controller.reset();
+    expect(controller.state, AnimatedTextState.reset);
+  });
+
+  test('Changing state directly via setter works', () {
+    controller.state = AnimatedTextState.pausedByUser;
+    expect(controller.state, AnimatedTextState.pausedByUser);
+  });
+}
diff --git a/test/smoke_test.dart b/test/smoke_test.dart
index c9772f3..e2b8399 100644
--- a/test/smoke_test.dart
+++ b/test/smoke_test.dart
@@ -18,7 +18,7 @@ void main() {
       final pumpCount = await tester.pumpAndSettle();
       print(' > ${example.label} pumped $pumpCount');
 
-      await tester.tap(find.byIcon(Icons.play_circle_filled));
+      await tester.tap(find.byIcon(Icons.arrow_right));
       await tester.pump();
     }