diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 5aed2e1..7eef216 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,9 +1,8 @@ name: Publish to pub.dev on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+*" # for tags like: 'v1.2.3' + release: + types: [published] jobs: publish: diff --git a/CHANGELOG.md b/CHANGELOG.md index 88658e6..0052505 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## 4.3.0 + +**New Animated Text** + +- **Scramble Animated Text**: Added scramble animation that displays text with a scrambling effect, where characters randomly change before settling to the final text. +- **Bounce Animated Text**: Added bounce animation that displays text with a bouncing effect, where text bounces in from below with an elastic curve. + ## 4.2.3 - **Feature:** Added `AnimatedTextController` to explicitly control animations. This allows developers more flexibility in managing animations. [#349](https://github.com/aagarwal1012/Animated-Text-Kit/pull/349) diff --git a/README.md b/README.md index b2fe4e8..12729b2 100644 --- a/README.md +++ b/README.md @@ -13,15 +13,6 @@ Pub Package - - Build Status - -
- - Codecov Coverage - License: MIT @@ -30,10 +21,6 @@ Awesome Flutter - - Donate -


@@ -56,9 +43,12 @@ - [TextLiquidFill](#textliquidfill) - [Wavy](#wavy) - [Flicker](#flicker) + - [Scramble](#scramble) + - [Bounce](#bounce) - [Create your own Animations](#create-your-own-animations) - [Bugs or Requests](#bugs-or-requests) - [Contributors](#contributors) +- [Star History](#star-history) # Flutter Package of the Week @@ -457,6 +447,60 @@ return SizedBox( ); ``` +## Scramble + + + +```dart +return SizedBox( + width: 250.0, + child: DefaultTextStyle( + style: const TextStyle( + fontSize: 40.0, + fontWeight: FontWeight.bold, + ), + child: AnimatedTextKit( + animatedTexts: [ + ScrambleAnimatedText( + 'Mobile Dev.', + speed: const Duration(milliseconds: 200), + ), + ScrambleAnimatedText('Explorer'), + ], + onTap: () { + print("Tap Event"); + }, + ), + ), +); +``` + +## Bounce + + + +```dart +return SizedBox( + width: 250.0, + child: DefaultTextStyle( + style: const TextStyle( + fontSize: 60.0, + fontWeight: FontWeight.bold, + ), + child: AnimatedTextKit( + animatedTexts: [ + BounceAnimatedText('Bounce!'), + BounceAnimatedText('Spring!'), + BounceAnimatedText('Jump!'), + ], + onTap: () { + print("Tap Event"); + }, + ), + ), +); +``` + ## Create your own Animations You can easily create your own animations by creating new classes that extend @@ -490,42 +534,14 @@ If you encounter any problems feel free to open an [issue](https://github.com/aa See [Contributing.md](https://github.com/aagarwal1012/Animated-Text-Kit/blob/master/CONTRIBUTING.md). -# Contributors - -Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Muhammed Salih Guler

🐛

Anders Cheow

🐛 🤔

Rohit Ashiwal

🐛

AdamSGit

🤔 🚧

Hemil Panchiwala

🚧 🤔 📖 💡

YiMing Han

🤔

Aayush Malhotra

🚧 🤔 🐛

Anthony Whitford

🤔 🚧

Jordy Wong

🐛

Darshan Rander

🤔 💻 🎨

Nsiah Akuoko Jeremiah

💻

Aniket Ambore

📖

Abhay V Ashokan

💻

Ritvij Kumar Sharma

💻

Koniiro

📖

Kalgi Sheth

💻 💡 📖

Mohit_007

📖
- - - - - - -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind are welcome! See [Contributing.md](https://github.com/aagarwal1012/Animated-Text-Kit/blob/master/CONTRIBUTING.md). +## Contributors + +Thanks to all our amazing contributors for their support and code! + +
+ + + +## Star History + +[![Star History Chart](https://api.star-history.com/svg?repos=aagarwal1012/Animated-Text-Kit&type=Date)](https://star-history.com/#aagarwal1012/Animated-Text-Kit&Date) diff --git a/display/bounce.gif b/display/bounce.gif new file mode 100644 index 0000000..6ec71d3 Binary files /dev/null and b/display/bounce.gif differ diff --git a/display/scramble.gif b/display/scramble.gif new file mode 100644 index 0000000..d5f3111 Binary files /dev/null and b/display/scramble.gif differ diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/example/ios/Flutter/AppFrameworkInfo.plist index 6b4c0f7..d57061d 100644 --- a/example/ios/Flutter/AppFrameworkInfo.plist +++ b/example/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 8.0 + 13.0 diff --git a/example/ios/Flutter/ephemeral/flutter_lldb_helper.py b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py new file mode 100644 index 0000000..a88caf9 --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_lldb_helper.py @@ -0,0 +1,32 @@ +# +# Generated file, do not edit. +# + +import lldb + +def handle_new_rx_page(frame: lldb.SBFrame, bp_loc, extra_args, intern_dict): + """Intercept NOTIFY_DEBUGGER_ABOUT_RX_PAGES and touch the pages.""" + base = frame.register["x0"].GetValueAsAddress() + page_len = frame.register["x1"].GetValueAsUnsigned() + + # Note: NOTIFY_DEBUGGER_ABOUT_RX_PAGES will check contents of the + # first page to see if handled it correctly. This makes diagnosing + # misconfiguration (e.g. missing breakpoint) easier. + data = bytearray(page_len) + data[0:8] = b'IHELPED!' + + error = lldb.SBError() + frame.GetThread().GetProcess().WriteMemory(base, data, error) + if not error.Success(): + print(f'Failed to write into {base}[+{page_len}]', error) + return + +def __lldb_init_module(debugger: lldb.SBDebugger, _): + target = debugger.GetDummyTarget() + # Caveat: must use BreakpointCreateByRegEx here and not + # BreakpointCreateByName. For some reasons callback function does not + # get carried over from dummy target for the later. + bp = target.BreakpointCreateByRegex("^NOTIFY_DEBUGGER_ABOUT_RX_PAGES$") + bp.SetScriptCallbackFunction('{}.handle_new_rx_page'.format(__name__)) + bp.SetAutoContinue(True) + print("-- LLDB integration loaded --") diff --git a/example/ios/Flutter/ephemeral/flutter_lldbinit b/example/ios/Flutter/ephemeral/flutter_lldbinit new file mode 100644 index 0000000..e3ba6fb --- /dev/null +++ b/example/ios/Flutter/ephemeral/flutter_lldbinit @@ -0,0 +1,5 @@ +# +# Generated file, do not edit. +# + +command script import --relative-to-command-file flutter_lldb_helper.py diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 1aec2aa..e06f452 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -127,7 +127,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1020; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { @@ -171,10 +171,12 @@ /* Begin PBXShellScriptBuildPhase section */ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); inputPaths = ( + "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", ); name = "Thin Binary"; outputPaths = ( @@ -185,6 +187,7 @@ }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -272,7 +275,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -354,7 +357,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -403,7 +406,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a28140c..fc5ae03 100644 --- a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ @@ -45,11 +46,13 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" + customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" debugDocumentVersioning = "YES" debugServiceExtension = "internal" + enableGPUValidationMode = "1" allowLocationSimulation = "YES"> diff --git a/example/ios/Runner/AppDelegate.swift b/example/ios/Runner/AppDelegate.swift index 70693e4..b636303 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import UIKit import Flutter -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index a060db6..4f68a2c 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -41,5 +41,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents + diff --git a/example/lib/main.dart b/example/lib/main.dart index 9e3ae09..5df9b16 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -169,11 +169,13 @@ List animatedTextExamples({VoidCallback? onTap}) { final typerController = AnimatedTextController(); final typewriterController = AnimatedTextController(); final scaleController = AnimatedTextController(); + final bounceController = AnimatedTextController(); final colorizeController = AnimatedTextController(); final textLiquidFillController = AnimatedTextController(); final wavyTextController = AnimatedTextController(); final flickerController = AnimatedTextController(); final combinationController = AnimatedTextController(); + final scrambleController = AnimatedTextController(); return [ AnimatedTextExample( @@ -315,6 +317,26 @@ List animatedTextExamples({VoidCallback? onTap}) { ), ), ), + AnimatedTextExample( + label: 'Bounce', + color: Colors.amber[700], + controller: bounceController, + child: DefaultTextStyle( + style: const TextStyle( + fontSize: 60.0, + fontWeight: FontWeight.bold, + ), + child: AnimatedTextKit( + animatedTexts: [ + BounceAnimatedText('Bounce!'), + BounceAnimatedText('Spring!'), + BounceAnimatedText('Jump!'), + ], + controller: bounceController, + onTap: onTap, + ), + ), + ), AnimatedTextExample( label: 'Colorize', color: Colors.blueGrey[50], @@ -448,5 +470,52 @@ List animatedTextExamples({VoidCallback? onTap}) { ], ), ), + AnimatedTextExample( + label: 'Scramble', + color: Colors.orange[800], + controller: rotateController, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + const SizedBox( + width: 20.0, + height: 100.0, + ), + const Text( + 'Im a', + style: TextStyle(fontSize: 43.0), + ), + const SizedBox( + width: 16.0, + height: 100.0, + ), + DefaultTextStyle( + style: TextStyle( + fontSize: 40.0, + ), + child: AnimatedTextKit( + animatedTexts: [ + ScrambleAnimatedText( + 'Mobile Dev.', + speed: const Duration( + milliseconds: 200, + ), + ), + ScrambleAnimatedText('Explorer'), + ], + controller: scrambleController, + onTap: onTap, + isRepeatingAnimation: true, + totalRepeatCount: 10, + ), + ), + ], + ), + ], + ), + ), ]; } diff --git a/lib/animated_text_kit.dart b/lib/animated_text_kit.dart index c10b450..ea5b6ad 100644 --- a/lib/animated_text_kit.dart +++ b/lib/animated_text_kit.dart @@ -12,3 +12,5 @@ export 'src/scale.dart'; export 'src/text_liquid_fill.dart'; export 'src/wavy.dart'; export 'src/flicker.dart'; +export 'src/scramble.dart'; +export 'src/bounce.dart'; diff --git a/lib/src/animated_text.dart b/lib/src/animated_text.dart index 72c5741..f2151b8 100644 --- a/lib/src/animated_text.dart +++ b/lib/src/animated_text.dart @@ -193,18 +193,21 @@ class AnimatedTextKitState extends State @override Widget build(BuildContext context) { final completeText = _currentAnimatedText.completeText(context); - return GestureDetector( - behavior: HitTestBehavior.opaque, - onTap: _onTap, - child: _animatedTextController.state == - AnimatedTextState.pausedBetweenAnimations || - !_controller.isAnimating - ? completeText - : AnimatedBuilder( - animation: _controller, - builder: _currentAnimatedText.animatedBuilder, - child: completeText, - ), + return Semantics( + label: _currentAnimatedText.text, + child: GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: _onTap, + child: _animatedTextController.state == + AnimatedTextState.pausedBetweenAnimations || + !_controller.isAnimating + ? completeText + : AnimatedBuilder( + animation: _controller, + builder: _currentAnimatedText.animatedBuilder, + child: completeText, + ), + ), ); } @@ -244,9 +247,9 @@ class AnimatedTextKitState extends State _currentAnimatedText = widget.animatedTexts[_index]; _controller = AnimationController( - duration: _currentAnimatedText.duration, - vsync: this, - ); + duration: _currentAnimatedText.duration, + vsync: this, + animationBehavior: AnimationBehavior.preserve); _currentAnimatedText.initAnimation(_controller); diff --git a/lib/src/bounce.dart b/lib/src/bounce.dart new file mode 100644 index 0000000..691bd5f --- /dev/null +++ b/lib/src/bounce.dart @@ -0,0 +1,126 @@ +import 'package:flutter/material.dart'; +import 'animated_text.dart'; + +/// Animated Text that displays a [Text] element with a bouncing effect. +/// +/// The text bounces in from below with an elastic curve, creating a playful +/// and dynamic entrance animation. +class BounceAnimatedText extends AnimatedText { + /// The height from which the text bounces in. + /// + /// By default it is set to 50.0. + final double bounceHeight; + + /// The [Curve] of the bounce animation. + /// + /// By default it is set to Curves.elasticOut for a natural bounce effect. + final Curve curve; + + BounceAnimatedText( + String text, { + TextAlign textAlign = TextAlign.start, + TextStyle? textStyle, + Duration duration = const Duration(milliseconds: 1500), + this.bounceHeight = 50.0, + this.curve = Curves.elasticOut, + }) : super( + text: text, + textAlign: textAlign, + textStyle: textStyle, + duration: duration, + ); + + late Animation _bounce; + late Animation _fade; + + @override + void initAnimation(AnimationController controller) { + _bounce = Tween(begin: bounceHeight, end: 0.0).animate( + CurvedAnimation( + parent: controller, + curve: curve, + ), + ); + + _fade = Tween(begin: 0.0, end: 1.0).animate( + CurvedAnimation( + parent: controller, + curve: const Interval(0.0, 0.5, curve: Curves.easeIn), + ), + ); + } + + @override + Widget animatedBuilder(BuildContext context, Widget? child) { + return Transform.translate( + offset: Offset(0, _bounce.value), + child: Opacity( + opacity: _fade.value, + child: textWidget(text), + ), + ); + } +} + +/// Animation that displays [text] elements with a bouncing effect, one at a time. +@Deprecated('Use AnimatedTextKit with BounceAnimatedText instead.') +class BounceAnimatedTextKit extends AnimatedTextKit { + BounceAnimatedTextKit({ + Key? key, + required List text, + TextAlign textAlign = TextAlign.start, + TextStyle? textStyle, + double bounceHeight = 50.0, + Duration duration = const Duration(milliseconds: 1500), + Duration pause = const Duration(milliseconds: 1000), + VoidCallback? onTap, + void Function(int, bool)? onNext, + void Function(int, bool)? onNextBeforePause, + VoidCallback? onFinished, + bool isRepeatingAnimation = true, + int totalRepeatCount = 3, + bool repeatForever = false, + bool displayFullTextOnTap = false, + bool stopPauseOnTap = false, + Curve curve = Curves.elasticOut, + }) : super( + key: key, + animatedTexts: _animatedTexts( + text, + textAlign, + textStyle, + duration, + bounceHeight, + curve, + ), + pause: pause, + displayFullTextOnTap: displayFullTextOnTap, + stopPauseOnTap: stopPauseOnTap, + onTap: onTap, + onNext: onNext, + onNextBeforePause: onNextBeforePause, + onFinished: onFinished, + isRepeatingAnimation: isRepeatingAnimation, + totalRepeatCount: totalRepeatCount, + repeatForever: repeatForever, + ); + + static List _animatedTexts( + List text, + TextAlign textAlign, + TextStyle? textStyle, + Duration duration, + double bounceHeight, + Curve curve, + ) => + text + .map((text) => BounceAnimatedText( + text, + textAlign: textAlign, + textStyle: textStyle, + duration: duration, + bounceHeight: bounceHeight, + curve: curve, + )) + .toList(); +} diff --git a/lib/src/scramble.dart b/lib/src/scramble.dart new file mode 100644 index 0000000..db54c8d --- /dev/null +++ b/lib/src/scramble.dart @@ -0,0 +1,118 @@ +import 'dart:async'; +import 'dart:math'; + +import 'package:animated_text_kit/animated_text_kit.dart'; +import 'package:flutter/material.dart'; + +class ScrambleAnimatedText extends AnimatedText { + // duration of single character + final Duration speed; + + ScrambleAnimatedText( + String text, { + TextAlign textAlign = TextAlign.start, + TextStyle? textStyle, + this.speed = const Duration(milliseconds: 500), + }) : super( + text: text, + textAlign: textAlign, + textStyle: textStyle, + duration: speed * text.characters.length, + ); + + late Animation _scrambleText; + + @override + void initAnimation(AnimationController controller) { + _scrambleText = CurveTween( + curve: Curves.linear, + ).animate(controller); + } + + @override + Widget animatedBuilder(context, child) { + // get the count / text text length base from the animation value + final count = (_scrambleText.value * textCharacters.length).round(); + + return RichText( + text: TextSpan( + children: List.generate( + count, + (index) { + return WidgetSpan( + child: ScrambledChar( + char: textCharacters.elementAt(index), + scrambleDuration: speed, + style: textStyle, + ), + ); + }, + ), + ), + ); + } +} + +class ScrambledChar extends StatefulWidget { + final String char; + final Duration scrambleDuration; + final TextStyle? style; + + const ScrambledChar({ + required this.char, + this.style, + required this.scrambleDuration, + }) : assert(char.length == 1, "Char need to only 1 character"); + + @override + State createState() => _ScrambledCharState(); +} + +class _ScrambledCharState extends State { + final Random _random = Random(); + static const String _allChar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + 'abcdefghijklmnopqrstuvwxyz' + '0123456789' + '!@#\$%^&*()_+-=[]{}|;:\'",.<>?/`~'; + + late String _displayChar; + late Timer _timer; + + @override + void initState() { + super.initState(); + _displayChar = _generateScrambledChar(widget.char); + _startScrambling(); + } + + @override + void dispose() { + _timer.cancel(); + super.dispose(); + } + + String _generateScrambledChar(String text) { + return _allChar[_random.nextInt(_allChar.length)]; + } + + void _startScrambling() { + int elapsed = 0; + _timer = Timer.periodic(const Duration(milliseconds: 50), (timer) { + if (elapsed >= widget.scrambleDuration.inMilliseconds) { + setState(() => _displayChar = widget.char); + timer.cancel(); + } else { + setState(() => _displayChar = _generateScrambledChar(widget.char)); + elapsed += 50; + } + }); + } + + @override + Widget build(BuildContext context) { + return Text( + _displayChar, + style: widget.style, + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 1fbd1cf..739a8f9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: animated_text_kit description: A flutter package project which contains a collection of cool and beautiful text animations. -version: 4.2.3 +version: 4.3.0 homepage: https://github.com/aagarwal1012/Animated-Text-Kit/ maintainer: Ayush Agarwal (@aagarwal1012)