diff --git a/CHANGELOG.md b/CHANGELOG.md
index 99a38ed..7bfe4ec 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,37 +1,108 @@
-## 0.2.3
-- Fix dart analysis issues.
-
-## 0.2.2
-- Clamp `OpacityEffect`, `ClipEffect`, and `ColorFilterEffect` values to 0.0 - 1.0 to prevent exceptions with
-curves that go outside of this range.
-- Add new `startImmediately` boolean to .animate() to allow for animations to start immediately without waiting for an
-initial change in the `trigger` object.
-- Improve documentation of `AnimatedEffect`.
-
-## 0.2.1
-- Fix exceptions being thrown when animation controller state is changed before completion.
-
-## 0.2.0
-- [BREAKING] Renamed `toggle` to `trigger` in .animate() to better reflect its purpose.
-- [BREAKING] Renamed `AnimatedEffect` to `EffectWidget` to better reflect its purpose.
-- [BREAKING] Renamed `EffectAnimationValue` to `EffectQuery` to better reflect its purpose.
-- [BREAKING] Replace `value` in `EffectQuery` with `linearValue` and `curvedValue` to allow more refined control over animations.
-- [BREAKING] Renamed `PostFrameWidget` to `PostFrame`.
-- Add new Rolling Text effect.
-- Add new shake effect.
-- Add new align effect.
-- Update all effect extension functions to add more functionality of the `from` state.
-- Add new extension functions that have default from states like slideIn/Out() and fadeIn/Out().
-- Add new `oneShot`, `animateAfter`, `resetAll` functions to allow for more control over animations.
-- Add new `repeat` parameter to animation functions to allow for repeating animations.
-- Add new `delay` parameter to animation functions to allow for delaying animations.
-- Add new `playIf` parameter to animation functions to allow for conditional animations.
-
-## 0.1.1
-
-- Minor doc updates.
-- Add example GIFs in readme.
-
-## 0.1.0
-
-- Initial Release.
+# Changelog
+
+All notable changes to the Hyper Effects package are documented in this file.
+
+## [0.3.0] - Dec 15, 2024
+
+### Added
+- **New Effects**
+ - Padding effect for dynamic padding adjustments.
+ - Global roll effect for universal rolling animations.
+ - Width & height factor support in align effect.
+- **Scroll Transition Enhancements**
+ - Additional event variables for finer control.
+ - Improved transition state management.
+- **Pointer Transition Features**
+ - `usePointerRouter` option for flexible pointer event handling.
+ - Enhanced pointer position tracking.
+- **New AnimatedEffect Properties**
+ - `resetValues` - Controls value reset behavior.
+ - `interruptable` - Manages animation interruption.
+ - `skipIf` - Conditional animation execution.
+ - `startState` - Initial animation state control.
+ - `transformHits` property for translate effect.
+ - `rotateIn` and `rotateOut` methods for rotate effect.
+- **Added New Examples**
+ - group_animation.dart
+ - rolling_app_bar_animation.dart
+ - rolling_pictures_animation.dart
+ - scroll_phase_slide.dart
+ - scroll_phase_blur.dart
+ - success_card_animation.dart
+
+### Changed
+- **Breaking Changes**
+ - Effect apply function's child parameter is now nullable.
+ - Text rolling API redesigned for consistency with other effects.
+ - New unified interface matching other animation effects.
+ - Previous text rolling methods have been deprecated.
+ - `startImmediately` replaced with more flexible `startState`.
+ - Removed unnecessary PostFrame callbacks from pointer transition logic.
+- **Improvements**
+ - Default blur effect state now starts un-blurred.
+ - Added `characterTapeBuilders` to `SymbolTapeStrategy` for customization.
+ - Fixed issues with scroll transitions to provide smoother and more consistent user experience.
+
+## [0.2.3] - Feb 2, 2024
+
+### Fixed
+- Resolved Dart analysis issues for better code quality
+
+## [0.2.2] - Feb 2, 2024
+
+### Added
+- New `startImmediately` boolean in .animate()
+- Improved documentation for `AnimatedEffect`
+
+### Fixed
+- Value clamping for:
+ - `OpacityEffect` (0.0 - 1.0)
+ - `ClipEffect` (0.0 - 1.0)
+ - `ColorFilterEffect` (0.0 - 1.0)
+- Prevents exceptions with out-of-range curves
+
+## [0.2.1] - Dec 28, 2023
+
+### Fixed
+- Animation controller state change exception handling
+
+## [0.2.0] - Dec 24, 2023
+
+### Added
+- **New Effects**
+ - Rolling Text effect for text animations
+ - Shake effect for vibration animations
+ - Align effect for alignment control
+- **Animation Control**
+ - `oneShot` function for immediate animations
+ - `animateAfter` for sequential animations
+ - `resetAll` for animation state reset
+ - Repeat parameter for cyclic animations
+ - Delay parameter for timed starts
+ - `playIf` for conditional execution
+
+### Changed
+- **Breaking Changes**
+ - Renamed:
+ - `toggle` β `trigger` in .animate()
+ - `AnimatedEffect` β `EffectWidget`
+ - `EffectAnimationValue` β `EffectQuery`
+ - `PostFrameWidget` β `PostFrame`
+ - Enhanced `EffectQuery` with `linearValue` and `curvedValue`
+- **Improvements**
+ - Updated effect extensions with `from` state support
+ - Added convenience methods (slideIn/Out, fadeIn/Out)
+
+## [0.1.1] - Oct 26, 2023
+
+### Changed
+- Documentation improvements
+- Added example GIFs in README
+
+## [0.1.0] - Oct 25, 2023
+
+### Added
+- Initial release of Hyper Effects
+- Core animation and effect system
+- Basic effect implementations
+- Documentation and examples
diff --git a/README.md b/README.md
index 8a94047..6b1a7d1 100644
--- a/README.md
+++ b/README.md
@@ -16,10 +16,10 @@ Demo: [hyper-effects-demo.web.app](https://hyper-effects-demo.web.app/)
* π **Animate Everything:** No animation controllers or tweening needed. Animate any widget with a line of code.
* π **Scroll Transitions:** Control how your widgets look based on their position in a scroll view.
* π **Pointer transitions:** No gesture detectors or state management required. Control how your widgets look based on
-pointer events.
+ pointer events.
* π **Easy Integration:** Missing an effect? The API is designed to be simple and easy to extend.
-## Examples
+## Examples
@@ -148,13 +148,44 @@ provided.
| RollingText | Creates a rolling animation from one text to another. |
| Shake | Applies a shake effect to a widget. |
| Align | Changes the alignment of a widget. |
+| Roll | Rolls a widget from one state to another. |
+| Padding | Adds padding to a widget. |
For details about each effect, please visit the source code of the effect.
Link: [hyper_effects/lib/src/effects](https://github.com/hyper-designed/hyper_effects/tree/main/lib/src/effects)
+### Methodology
+
+Hyper Effects is not an animation tweening library. It's a library that provides a convenient way to interpolate
+between a large set of values for a widget. It's inspired by SwiftUI's syntax and aims to provide a similar experience
+in Flutter. With that knowledge in mind, you can get very far with Hyper Effects when it comes to animation
+orchestration, but it's not a replacement for a full-fledged animation library like Rive, Flare, or even a good ol'
+TweenSequence from Flutter's animation framework.
+
+It's important to understand that the way Hyper Effects works is by lerping two values at any given point in time,
+therefore, a complex sequenced animation may not have enough context to orchestrate proper state management when
+chaining multiple animations.
+
+To understand exactly how it works and where to draw the line, read the docs and see the examples.
+
+Points to keep in mind:
+
+1. Treat our extensions as widget "shortcuts" more than anything else. They simply wrap your widget with a specific
+ effect and use the value you provide. IE: Instead of wrapping a widget with an `Opacity` widget, you can use the
+ `opacity` extension.
+ You can effectively use the extensions as shortcuts without ever animating anything.
+2. By doing this, the framework now has a special Opacity-looking widget that has the ability to consume a time value
+ to interpolate to different values you give that opacity extension.
+3. To actually get yourself a time value, you need to use the `animate` method on the widget. This method will take a
+ trigger value that will trigger the animation when it changes. Alternatively, a transition can be used to provide a
+ continuous value to the widget.
+
+These are the core concepts of how the library works. It's important to understand these concepts to get the most out of
+Hyper Effects.
+
### Animations
-To animate the effects, you need to call the `animate` method on the widget like so:
+To animate any effect, you need to use the `animate` method on the widget like so:
```dart
@override
@@ -168,19 +199,23 @@ Widget build(BuildContext context) {
```
`trigger` is a parameter of type `Object`. It's inspired from SwiftUI's `value` parameter on its `animation` modifier.
-Whenever the value of `trigger` changes, the effect will animate to the new value. In this case, `myCondition` is the
-trigger value. You can use any object as a trigger value, but you will likely want to use the same object that you use
-to control the condition of the effect as it is the point in which the effect should animate.
+Whenever the value of `trigger` changes, the effect will animate to the new value using an internal `AnimatedEffect`
+widget with its own `AnimationController`.
+
+In this case, `myCondition` is the trigger value. You can use any object as a trigger value, but you will likely want
+to use the same object that you use to control the condition of the effect as it is the point in which the effect
+should animate.
In simpler words, `trigger` takes any object. When the value of the object changes to anything else, the effect will
animate to the new value. The animation will never play if the value of the trigger is the same as the previous value.
+The internal `AnimationController` is driven based on changes to the trigger object.
> Note: If you want the animation to trigger immediately when the widget is built and then allow it to be triggered
-> again later, you can use the `startImmediately` parameter in the `animate` method. This will start the animation
-> immediately without waiting for an initial change in the `trigger` object. Subsequent changes in the `trigger` object
-> will still trigger the animation as normal.
+> again later, you can change the `startState` parameter in the `animate` method to AnimationStartState.playImmediately.
+> This will start the animation immediately without waiting for an initial change in the `trigger` object. Subsequent
+> changes in the `trigger` object will still trigger the animation as normal.
-HyperEffects takes heavy inspiration from SwiftUI in that it attempts to provide Apple-like default values for
+Hyper Effects takes heavy inspiration from SwiftUI in that it attempts to provide Apple-like default values for
everything, that means that by default, animations are of 350ms duration and use Apple's custom easeInOut curve. The
result is a very smooth and natural feeling animation that is very reminiscent of Apple's style.
@@ -195,11 +230,11 @@ Widget build(BuildContext context) {
)
.opacity(myCondition ? 0 : 1)
.animate(
- trigger: myCondition,
- duration: const Duration(milliseconds: 200),
- curve: Curves.easeOutQuart,
- startImmediately: true,
- );
+ trigger: myCondition,
+ duration: const Duration(milliseconds: 200),
+ curve: Curves.easeOutQuart,
+ startImmediately: true,
+ );
}
```
@@ -219,7 +254,7 @@ Widget build(BuildContext context) {
}
```
-Since these are convenient extensions, you can use the effects as Widgets using the `apply` method like so:
+Since these are convenient extensions, you can use the effects as Widgets directly using the `apply` method like so:
```dart
@override
@@ -250,6 +285,8 @@ Widget build(BuildContext context) {
}
```
+This is what the Widget tree looks like internally when you use this library.
+
#### Properties
* trigger: The value used to trigger the animation. As long as the value of trigger is the same, the animation will not
@@ -262,8 +299,16 @@ Widget build(BuildContext context) {
* delay: A delay before the animation starts.
* playIf: A callback that returns whether the animation should be played or skipped. If the callback returns false, the
animation will be skipped, even when it is explicitly triggered.
-* startImmediately: A boolean property that determines whether the animation should start immediately without waiting for
- an initial change in the trigger object.
+* resetValues: Normally, an effect represents the current state of the widget and this
+ animate effect is only in charge of lerping between states of those effect values.
+ If this is set to true, instead of treating effects as current states to animate between, it will always animate from
+ an initial default state towards the current state.
+* interruptable: Whether the animation should be reset on subsequent triggers. If this animation is re-triggered, it
+ will reset the current active animation and re-drive from the beginning.
+ Setting this to true will force the animation to wait for the last animation in the chain to finish before starting.
+* startState: Determines the behavior of the animation as soon as it is added to the widget tree.
+* skipIf: A callback that determines whether the animation should be skipped. If the callback returns true, the animation
+ will be skipped entirely.
### One Shot Animations
@@ -281,7 +326,7 @@ Widget build(BuildContext context) {
color: Colors.blue,
).slideInFromBottom()
.oneShot(
- // All the normal parameters inside of .animate() but without the trigger parameter.
+ // All the normal parameters inside of .animate() but without the trigger and startState parameters.
);
}
```
@@ -289,12 +334,13 @@ Widget build(BuildContext context) {
### Animate After Animations
The `animateAfter` function triggers the animation after the last animation in the chain ends.
-It's useful when you want to create a sequence of animations where one animation starts after the previous one ends.
+It's useful when you want to create a simple sequence of animations where one animation starts after the previous one
+ends.
Here's an example of how to use the `animateAfter` function:
```dart
- @override
+@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
@@ -335,7 +381,9 @@ See this example in action in the demo app: [hyper-effects-demo.web.app](https:/
Using `resetAll` at the end of the chain of animations will reset all the effects in the chain to their original values.
-When the animation is triggered again, all the effects will animate from their original values to the new values.
+When the animation is triggered again, all the effects will animate from their original values to the new values. In
+other words, each individual Hyper Effects AnimationController in the descendent widget tree will be reset to its
+initial state.
### Delayed Animations
@@ -350,8 +398,8 @@ Widget build(BuildContext context) {
return Container(
color: Colors.blue,
)
- .opacity(myCondition ? 0 : 1)
- .animate(
+ .opacity(myCondition ? 0 : 1)
+ .animate(
trigger: myCondition,
delay: const Duration(seconds: 1), // 1 second delay before the animation starts after the trigger changes.
);
@@ -376,8 +424,8 @@ Widget build(BuildContext context) {
return Container(
color: Colors.blue,
)
- .opacity(myCondition ? 0 : 1)
- .animate(
+ .opacity(myCondition ? 0 : 1)
+ .animate(
trigger: myCondition,
repeat: 3, // The animation will repeat 3 times
);
@@ -396,6 +444,16 @@ The Rolling Text feature in Hyper Effects allows you to create a rolling animati
character rolls individually to form the new text. This feature provides a visually appealing way to transition between
different text states in your application.
+#### Limitations
+
+This effect reconstructs each individual symbol in the Text widget into vertical tapes of symbols that are painted
+using a custom painter. This means that the Rolling Text feature is not suitable for large text bodies. It is
+recommended to use this feature for short text strings. Each individual strip of characters is animated independently
+and can be customized to create a unique rolling effect.
+
+This .roll() effect, when applied to non-Text widgets, will work as expected as it does not need to reconstruct
+anything.
+
#### Usage
To use the Rolling Text feature, you can use the roll extension on any Text widget. Here's an example:
@@ -410,18 +468,52 @@ Widget build(BuildContext context) {
In this example, the text will roll from 'Hello' to 'World'. Each character in 'Hello' will roll until it changes to the
corresponding character in 'World'.
+For non-text widgets, you can use the roll effect similarly:
+
+```dart
+@override
+Widget build(BuildContext context) {
+ return Container(
+ width: 100,
+ height: 100,
+ color: Colors.blue,
+ ).roll(
+ from: const Offset(0, 0),
+ to: const Offset(0, 100),
+ axis: Axis.vertical,
+ );
+}
+```
+
#### Customization
The Rolling Text feature provides several options for customization:
-- padding: This option allows you to set the internal padding between the row of symbol tapes and the clipping mask.
-- tapeStrategy: This option determines the string of characters to create and roll through for each character index
+- padding: Allows you to set the internal padding between the row of symbol tapes and the clipping mask.
+- tapeStrategy: Determines the string of characters to create and roll through for each character index
between the old and new text.
-- tapeCurve: This option determines the curve each roll of symbol tape uses to slide up and down through its characters.
-- tapeSlideDirection: This option determines the direction each roll of symbol tape slides through its characters.
-- staggerTapes: This option determines whether the tapes should be staggered or not.
-- staggerSoftness: This option determines how harsh the stagger effect is.
-- reverseStaggerDirection: This option determines whether the stagger effect should be reversed or not.
+- tapeCurve: Determines the curve each roll of symbol tape uses to slide up and down through its characters.
+- tapeSlideDirection: Determines the direction each roll of symbol tape slides through its characters.
+- staggerTapes: Determines whether the tapes should be staggered or not.
+- staggerSoftness: Determines how harsh the stagger effect is.
+- reverseStaggerDirection: Determines whether the stagger effect should be reversed or not.
+- symbolDistanceMultiplier: Determines the height of each symbol in each tape relative to the font size. If the font
+ size is 32, the final height of the entire widget is fontSize * lineHeightMultiplier. The default multiplier is 1.
+- fixedTapeWidth: Can be optionally used to set a fixed width for each tape. If null, the width of each tape will be
+ the width of the active character in the tape. If not null, the width of each tape will be the fixed width provided.
+ Note that this will allow the text's characters to potentially overlap each other.
+- widthDuration: Determines the duration of the width animation. If null, the same duration is used as the one
+ provided to the `animate` function.
+- widthCurve: Determines the curve of the width animation of each tape. If null, the same curve is used as the one
+ provided to the `animate` function.
+- characterTapeBuilders: Custom builders for individual character tapes, allowing for fine-grained control over how each
+ character is displayed and animated.
+
+For the general roll effect, additional options include:
+- axis: The axis along which the rolling animation occurs (horizontal or vertical).
+- from: The starting offset or state.
+- to: The ending offset or state.
+- transformHits: Whether hit testing should be transformed with the roll animation.
Here's an example of how to use these options:
@@ -487,110 +579,128 @@ Scroll transitions are transitions that are based on the position of a widget in
```dart
@override
- Widget build(BuildContext context) {
- return ListView.builder(
- itemBuilder: (context, index) {
- return Container(
- width: 350,
- height: 100,
- margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
- alignment: Alignment.center,
- decoration: BoxDecoration(
- color: randomColor(index),
- borderRadius: BorderRadius.circular(16),
+Widget build(BuildContext context) {
+ return ListView.builder(
+ itemBuilder: (context, index) {
+ return Container(
+ width: 350,
+ height: 100,
+ margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: randomColor(index),
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: Text(
+ 'Item $index',
+ style: const TextStyle(
+ fontSize: 24,
+ color: Colors.white,
),
- child: Text(
- 'Item $index',
- style: const TextStyle(
- fontSize: 24,
- color: Colors.white,
+ ),
+ ).scrollTransition(
+ (context, widget, event) =>
+ widget
+ .blur(
+ switch (event.phase) {
+ ScrollPhase.identity => 0,
+ ScrollPhase.topLeading => 10,
+ ScrollPhase.bottomTrailing => 10,
+ },
+ )
+ .scale(
+ switch (event.phase) {
+ ScrollPhase.identity => 1,
+ ScrollPhase.topLeading => 0.9,
+ ScrollPhase.bottomTrailing => 0.9,
+ },
),
- ),
- ).scrollTransition(
- (context, widget, event) => widget
- .blur(
- switch (event.phase) {
- ScrollPhase.identity => 0,
- ScrollPhase.topLeading => 10,
- ScrollPhase.bottomTrailing => 10,
- },
- )
- .scale(
- switch (event.phase) {
- ScrollPhase.identity => 1,
- ScrollPhase.topLeading => 0.9,
- ScrollPhase.bottomTrailing => 0.9,
- },
- ),
- );
- },
- );
- }
+ );
+ },
+ );
+}
```
In a scroll transition, the `phase` is a `ScrollPhase` enum that determines the view-state of a widget in a scroll view.
+
- `ScrollPhase.identity` is when the widget is fully viewable in the scroll view.
-- `ScrollPhase.topLeading` is when the widget is partially viewable from the top/left, IE: It's leaving the top or left
-of the scroll view.
-- `ScrollPhase.bottomTrailing` is when the widget is partially viewable from the bottom/right, IE: It's leaving the
-bottom or right of the scroll view.
-
-In addition to the ScrollPhase, the `event` also provides `screenOffsetFraction` which is a double value between -1, 0
-and 1 that represents the progress the scroll view is moving away from the center of the scroll view.
-- If the widget is near the center of the scroll view, the value tends towards 0.
-- As the widget moves towards the ceiling of the scroll view, the value tends towards 1. It clamps to 1 when the item is
-fully out of the scroll view.
-- As the item moves towards the floor of the scroll view, the value tends towards -1. It clamps to -1 when the item is
-fully out of the scroll view.
+- `ScrollPhase.topLeading` is when the widget is partially viewable from the top/left, IE: It's leaving the top or left
+ of the scroll view.
+- `ScrollPhase.bottomTrailing` is when the widget is partially viewable from the bottom/right, IE: It's leaving the
+ bottom or right of the scroll view.
+
+Other properties of the scroll transition:
+
+- phaseOffsetFraction: The current progress an element's phase is going through. If `phase` is identity this value is 1.
+ If `phase` is topLeading, it goes from 0 towards 1 as it leaves the screen. If `phase` is bottomTrailing, it goes from
+ 0 towards 1 as it enters the screen.
+- phaseOffsetFraction: The current progress an element is going through the scrolling viewport. If the item is near
+ the center of the scroll view, the value tends towards 0. As the item moves towards the ceiling of the scroll view,
+ the value tends towards 1. It clamps to 1 when the item is fully out of the scroll view. As the item moves towards the
+ floor of the scroll view, the value tends towards -1. It clamps to -1 when the item is fully out of the scroll view.
+ final double screenOffsetFraction;
+- scrollPixels: The number of pixels scrolled inside of the parent scroll view.
+- viewportSize: The height or width of the parent scroll view.
+- scrollDelta: The change in scroll position since the last update.
+- scrollDirection: The direction the scroll view is being scrolled to.
+- pointerPosition: The position of the pointer device in the global coordinate space.
+- distanceFromPointer: The distance from the pointer device to the center of this widget.
+- visualIndex: The visual index is the index in which the item is displayed in the scroll view. For example, instead of
+ the regular index of a list, if you scroll down, the first item that is visible inside the scroll view will have some
+ arbitrary index, but the visual index would be 0 as it is the index that is perceived by the user.
+- reverseVisualIndex: The `visualIndex` calculated in the opposite direction.
#### Caution
+
Be careful when using `screenOffsetFraction` as it can be a bit tricky to use. The way transitions work
-internally is that they lerp to and from a specific internal value. That value for scroll transitions is the
+internally is that they lerp to and from a specific internal value. That value for scroll transitions is the
ScrollPhase.
-With that in mind, despite the fact that you can use `phase` to animte your widgets easily with the extensions, for
+With that in mind, despite the fact that you can use `phase` to animte your widgets easily with the extensions, for
other properties like`screenOffsetFraction`, you will need to use the manual Effects API to animate them.
```dart
@override
- Widget build(BuildContext context) {
- return ListView.builder(
- itemBuilder: (context, index) {
- return Container(
- width: 350,
- height: 100,
- margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
- alignment: Alignment.center,
- decoration: BoxDecoration(
- color: randomColor(index),
- borderRadius: BorderRadius.circular(16),
- ),
- child: Text(
- 'Item $index',
- style: const TextStyle(
- fontSize: 24,
- color: Colors.white,
- ),
+Widget build(BuildContext context) {
+ return ListView.builder(
+ itemBuilder: (context, index) {
+ return Container(
+ width: 350,
+ height: 100,
+ margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
+ alignment: Alignment.center,
+ decoration: BoxDecoration(
+ color: randomColor(index),
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: Text(
+ 'Item $index',
+ style: const TextStyle(
+ fontSize: 24,
+ color: Colors.white,
),
- ).scrollTransition((context, widget, event) => TransformEffect(
- rotateX: -90 * event.screenOffsetFraction * pi / 180,
- translateY: (event.screenOffsetFraction * -1) * 200,
- translateZ: event.screenOffsetFraction.abs() * 100,
- scaleX: 1 - (event.screenOffsetFraction.abs() / 2),
- depth: 0.002,
- ).apply(context, widget));
- },
- );
- }
+ ),
+ ).scrollTransition((context, widget, event) =>
+ TransformEffect(
+ rotateX: -90 * event.screenOffsetFraction * pi / 180,
+ translateY: (event.screenOffsetFraction * -1) * 200,
+ translateZ: event.screenOffsetFraction.abs() * 100,
+ scaleX: 1 - (event.screenOffsetFraction.abs() / 2),
+ depth: 0.002,
+ ).apply(context, widget));
+ },
+ );
+}
```
-When using the manual Effects API directly, you can use any parameter and manipulate their values as much as you like in the above example, whereas with the convenient extensions API, you may be limited to using the internally
+When using the manual Effects API directly, you can use any parameter and manipulate their values as much as you like in
+the above example, whereas with the convenient extensions API, you may be limited to using the internally
animated value. In scroll transitions, the internally animated value is the `phase` property.
#### Pointer Transitions
Pointer transitions are transitions that provide events based on the pointer's position either locally or globally
-depending on configuration. This transition has many configurable parameters that allow you to control how the
+depending on configuration. This transition has many configurable parameters that allow you to control how the
transition should output its values in its builder.
```dart
@@ -601,11 +711,12 @@ Widget build(BuildContext context) {
color: Colors.blue,
),
).pointerTransition(
- (context, child, event) => child.translateXY(
- event.valueOffset.dx / 2,
- event.valueOffset.dy / 2,
- fractional: true,
- ),
+ (context, child, event) =>
+ child.translateXY(
+ event.valueOffset.dx / 2,
+ event.valueOffset.dy / 2,
+ fractional: true,
+ ),
);
}
```
@@ -620,7 +731,7 @@ A value of 1 or -1 is when the pointer device is at the farthest point from the
This value is the average of the `valueOffset`'s x and y values.
`valueOffset` is an `Offset` parameter very similar to `value` but provides more information about the individual x and
-y axes. It's The distance of the pointer device from the `origin` as an offset.
+y axes. It's The distance of the pointer device from the `origin` as an offset.
A value offset of (0, 0) is when the pointer device is at the `origin`.
A value offset of (1, 1) is when the pointer device is at the farthest point from the `origin`.
@@ -635,33 +746,37 @@ When using the pointer transition, you can provide multiple parameters to contro
in the `event` provided to the builder.
```dart
- Widget pointerTransition(
- PointerTransitionBuilder builder, {
- Alignment origin = Alignment.center,
- bool useGlobalPointer = false,
- bool transitionBetweenBounds = true,
- bool resetOnExitBounds = true,
- Curve curve = appleEaseInOut,
- Duration duration = const Duration(milliseconds: 125),
- }) {
- return PointerTransition(
- builder: builder,
- origin: origin,
- useGlobalPointer: useGlobalPointer,
- transitionBetweenBounds: transitionBetweenBounds,
- resetOnExitBounds: resetOnExitBounds,
- curve: curve,
- duration: duration,
- child: this,
- );
- }
+Widget pointerTransition(PointerTransitionBuilder builder, {
+ Alignment origin = Alignment.center,
+ bool useGlobalPointer = false,
+ bool usePointerRouter = true,
+ bool transitionBetweenBounds = true,
+ bool resetOnExitBounds = true,
+ Curve curve = appleEaseInOut,
+ Duration duration = const Duration(milliseconds: 125),
+ Key? key,
+}) {
+ return PointerTransition(
+ key: key,
+ builder: builder,
+ origin: origin,
+ useGlobalPointer: useGlobalPointer,
+ usePointerRouter: usePointerRouter,
+ transitionBetweenBounds: transitionBetweenBounds,
+ resetOnExitBounds: resetOnExitBounds,
+ curve: curve,
+ duration: duration,
+ child: this,
+ );
+}
```
`origin` is an `Alignment` parameter that determines the origin to transform the pointer position around.
-- If the origin is set to `Alignment.center`, as the pointer moves away from the center of the screen,
-the `value` will increase.
-- If the origin is set to `Alignment.topLeft`, as the pointer moves away from the top left corner of the screen,
-the `value` will increase.
+
+- If the origin is set to `Alignment.center`, as the pointer moves away from the center of the screen,
+ the `value` will increase.
+- If the origin is set to `Alignment.topLeft`, as the pointer moves away from the top left corner of the screen,
+ the `value` will increase.
`useGlobalPointer` is a `boolean` parameter that determines whether the pointer position should be calculated globally
or locally. If set to true, the pointer position will be calculated from the top left corner of the screen. If set to
@@ -669,7 +784,7 @@ false, the pointer position will be calculated from the top left corner of the w
`transitionBetweenBounds` is a `boolean` parameter that determines whether the transition should animate between the
bounds of the widget or not. If set to true, the transition will trigger the internal animation to reset when the
-pointer moves in and out of the bounds of the widget. If set to false, the transition will snap instantly to
+pointer moves in and out of the bounds of the widget. If set to false, the transition will snap instantly to
the new values when the pointer moves in and out of the bounds of the widget.
`resetOnExitBounds` is a `boolean` parameter that determines whether this transition should reset the position of the
@@ -690,7 +805,8 @@ other properties and expect them to transition perfectly, you may get unexpected
## Contributing
-You are welcome to contribute on this package. See [CONTRIBUTING.md](https://github.com/hyper-designed/hyper_effects/blob/main/CONTRIBUTING.md) for details.
+You are welcome to contribute on this package.
+See [CONTRIBUTING.md](https://github.com/hyper-designed/hyper_effects/blob/main/CONTRIBUTING.md) for details.
## Authors
diff --git a/example/.gitignore b/example/.gitignore
index 24476c5..6c31954 100644
--- a/example/.gitignore
+++ b/example/.gitignore
@@ -5,9 +5,11 @@
*.swp
.DS_Store
.atom/
+.build/
.buildlog/
.history
.svn/
+.swiftpm/
migrate_working_dir/
# IntelliJ related
diff --git a/example/.metadata b/example/.metadata
index d363751..9eeac39 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: "ead455963c12b453cdb2358cad34969c76daf180"
+ revision: "8495dee1fd4aacbe9de707e7581203232f591b2f"
channel: "stable"
project_type: app
@@ -13,14 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
- create_revision: ead455963c12b453cdb2358cad34969c76daf180
- base_revision: ead455963c12b453cdb2358cad34969c76daf180
+ create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f
+ base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f
- platform: macos
- create_revision: ead455963c12b453cdb2358cad34969c76daf180
- base_revision: ead455963c12b453cdb2358cad34969c76daf180
- - platform: web
- create_revision: ead455963c12b453cdb2358cad34969c76daf180
- base_revision: ead455963c12b453cdb2358cad34969c76daf180
+ create_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f
+ base_revision: 8495dee1fd4aacbe9de707e7581203232f591b2f
# User provided section
diff --git a/example/assets/fashion/fashion_0.jpg b/example/assets/fashion/fashion_0.jpg
index 619128a..0816428 100644
Binary files a/example/assets/fashion/fashion_0.jpg and b/example/assets/fashion/fashion_0.jpg differ
diff --git a/example/assets/fashion/fashion_1.jpg b/example/assets/fashion/fashion_1.jpg
index 68f6284..cb11359 100644
Binary files a/example/assets/fashion/fashion_1.jpg and b/example/assets/fashion/fashion_1.jpg differ
diff --git a/example/assets/fashion/fashion_2.jpg b/example/assets/fashion/fashion_2.jpg
index 3f4140f..8788d46 100644
Binary files a/example/assets/fashion/fashion_2.jpg and b/example/assets/fashion/fashion_2.jpg differ
diff --git a/example/assets/fashion/fashion_3.jpg b/example/assets/fashion/fashion_3.jpg
index e8545bb..9157b28 100644
Binary files a/example/assets/fashion/fashion_3.jpg and b/example/assets/fashion/fashion_3.jpg differ
diff --git a/example/assets/fashion/fashion_4.jpg b/example/assets/fashion/fashion_4.jpg
index 48909f1..d36a7f7 100644
Binary files a/example/assets/fashion/fashion_4.jpg and b/example/assets/fashion/fashion_4.jpg differ
diff --git a/example/assets/fashion/fashion_5.jpg b/example/assets/fashion/fashion_5.jpg
index 74e5566..c3da6b0 100644
Binary files a/example/assets/fashion/fashion_5.jpg and b/example/assets/fashion/fashion_5.jpg differ
diff --git a/example/assets/fashion/fashion_6.jpg b/example/assets/fashion/fashion_6.jpg
index 02e2a21..a7f13c0 100644
Binary files a/example/assets/fashion/fashion_6.jpg and b/example/assets/fashion/fashion_6.jpg differ
diff --git a/example/assets/fashion/fashion_7.jpg b/example/assets/fashion/fashion_7.jpg
index 9f21d16..dbe22f7 100644
Binary files a/example/assets/fashion/fashion_7.jpg and b/example/assets/fashion/fashion_7.jpg differ
diff --git a/example/assets/fashion/fashion_8.jpg b/example/assets/fashion/fashion_8.jpg
index 02e3f8d..d911850 100644
Binary files a/example/assets/fashion/fashion_8.jpg and b/example/assets/fashion/fashion_8.jpg differ
diff --git a/example/lib/main.dart b/example/lib/main.dart
index 1638dcb..d004e83 100644
--- a/example/lib/main.dart
+++ b/example/lib/main.dart
@@ -7,11 +7,15 @@ import 'package:flutter/services.dart';
import 'package:flutter_box_transform/flutter_box_transform.dart';
import 'package:hyper_effects_demo/stories/color_filter_scroll_transition.dart';
import 'package:hyper_effects_demo/stories/counter_app.dart';
+import 'package:hyper_effects_demo/stories/group_animation.dart';
import 'package:hyper_effects_demo/stories/one_shot_reset_animation.dart';
-import 'package:hyper_effects_demo/stories/scroll_phase_transition.dart';
-import 'package:hyper_effects_demo/stories/scroll_wheel_blur.dart';
+import 'package:hyper_effects_demo/stories/rolling_app_bar_animation.dart';
+import 'package:hyper_effects_demo/stories/rolling_pictures_animation.dart';
+import 'package:hyper_effects_demo/stories/scroll_phase_blur.dart';
+import 'package:hyper_effects_demo/stories/scroll_phase_slide.dart';
import 'package:hyper_effects_demo/stories/scroll_wheel_transition.dart';
import 'package:hyper_effects_demo/stories/shake_and_spring_animation.dart';
+import 'package:hyper_effects_demo/stories/success_card_animation.dart';
import 'package:hyper_effects_demo/stories/text_animation.dart';
import 'package:hyper_effects_demo/stories/windows_settings_transition.dart';
@@ -32,7 +36,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(
brightness: Brightness.light,
seedColor: Colors.blue,
- background: const Color(0xFFF0F0F0),
+ surface: const Color(0xFFF0F0F0),
),
inputDecorationTheme: InputDecorationTheme(
isDense: true,
@@ -48,7 +52,7 @@ class MyApp extends StatelessWidget {
colorScheme: ColorScheme.fromSeed(
brightness: Brightness.dark,
seedColor: Colors.blue,
- background: const Color(0xFF0F0F0F),
+ surface: const Color(0xFF0F0F0F),
),
inputDecorationTheme: InputDecorationTheme(
isDense: true,
@@ -80,6 +84,11 @@ class Storyboard extends StatefulWidget {
class _StoryboardState extends State with WidgetsBindingObserver {
final List animationStories = [
+ const Story(title: 'Success Card Animation', child: SuccessCardAnimation()),
+ const Story(
+ title: 'Group Animation',
+ child: GroupAnimation(),
+ ),
const Story(
title: 'Text Rolling Animations',
child: TextAnimation(),
@@ -96,6 +105,14 @@ class _StoryboardState extends State with WidgetsBindingObserver {
title: 'Spring Animation',
child: SpringAnimation(),
),
+ const Story(
+ title: 'Rolling Pictures Animation',
+ child: RollingWidgetAnimation(),
+ ),
+ const Story(
+ title: 'Rolling App Bar Animation',
+ child: RollingAppBarAnimation(),
+ ),
];
final List transitionStories = [
const Story(
diff --git a/example/lib/stories/color_filter_scroll_transition.dart b/example/lib/stories/color_filter_scroll_transition.dart
index ce904ee..13bf211 100644
--- a/example/lib/stories/color_filter_scroll_transition.dart
+++ b/example/lib/stories/color_filter_scroll_transition.dart
@@ -26,17 +26,18 @@ class _FashionScrollTransitionState extends State {
itemExtent: 300,
cacheExtent: 300,
itemBuilder: (context, index) {
- return Padding(
+ return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
+ alignment: Alignment.center,
child: Container(
- width: 350,
+ width: 500,
height: 300,
alignment: Alignment.center,
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/fashion/fashion_${index % 8}.jpg'),
- fit: BoxFit.cover,
+ fit: BoxFit.fitWidth,
),
borderRadius: BorderRadius.circular(16),
),
diff --git a/example/lib/stories/counter_app.dart b/example/lib/stories/counter_app.dart
index de56228..10952b1 100644
--- a/example/lib/stories/counter_app.dart
+++ b/example/lib/stories/counter_app.dart
@@ -1,5 +1,3 @@
-import 'dart:math';
-
import 'package:flutter/material.dart';
import 'package:hyper_effects/hyper_effects.dart';
@@ -34,9 +32,9 @@ class _CounterAppState extends State {
'You have pushed the button this many times:',
),
Text(
- '${max(0, _counter - 1)}',
+ '$_counter',
style: Theme.of(context).textTheme.headlineMedium,
- ).roll('$_counter').animate(trigger: _counter),
+ ).roll().animate(trigger: _counter),
],
),
),
diff --git a/example/lib/stories/group_animation.dart b/example/lib/stories/group_animation.dart
new file mode 100644
index 0000000..3dbf348
--- /dev/null
+++ b/example/lib/stories/group_animation.dart
@@ -0,0 +1,138 @@
+import 'package:flutter/material.dart';
+import 'package:hyper_effects/hyper_effects.dart';
+
+class GroupAnimation extends StatefulWidget {
+ const GroupAnimation({super.key});
+
+ @override
+ State createState() => _GroupAnimationState();
+}
+
+class _GroupAnimationState extends State {
+ int counter = 0;
+
+ final List tags1 = [
+ 'family',
+ 'friends',
+ 'work',
+ 'school',
+ 'sports',
+ 'hobbies',
+ ];
+ final List tags2 = [
+ 'family',
+ 'work',
+ 'friends',
+ 'sports',
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+ final tags = counter % 2 == 0 ? tags1 : tags2;
+ return Center(
+ child: Column(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Container(
+ height: 40,
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surface,
+ borderRadius: BorderRadius.circular(20),
+ ),
+ clipBehavior: Clip.antiAlias,
+ child: AnimatedGroup(
+ triggerAddImmediately: false,
+ builder: (context, children) => Row(children: children),
+ children: [
+ for (int i = 0; i < tags.length; i++)
+ Padding(
+ key: ValueKey(tags[i]),
+ padding: const EdgeInsets.symmetric(horizontal: 8),
+ child: TagChip(
+ tag: tags[i],
+ selected: false,
+ ),
+ )
+ ],
+ ),
+ ),
+ ElevatedButton(
+ onPressed: () {
+ setState(() {
+ counter++;
+ });
+ },
+ child: const Text('Switch'),
+ ),
+ ],
+ ),
+ );
+ }
+}
+
+class TagChip extends StatefulWidget {
+ final String tag;
+ final bool selected;
+ final VoidCallback? onTap;
+ final bool compact;
+ final Widget? suffix;
+
+ const TagChip({
+ super.key,
+ required this.tag,
+ required this.selected,
+ this.onTap,
+ this.compact = false,
+ this.suffix,
+ });
+
+ @override
+ State createState() => _TagChipState();
+}
+
+class _TagChipState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ decoration: BoxDecoration(
+ color: widget.selected
+ ? Theme.of(context).colorScheme.primary.withOpacity(0.1)
+ : Theme.of(context).colorScheme.onSurface.withOpacity(0.1),
+ borderRadius: BorderRadius.circular(6),
+ ),
+ clipBehavior: Clip.none,
+ child: InkWell(
+ borderRadius: BorderRadius.circular(6),
+ onTap: widget.onTap,
+ child: Padding(
+ padding: const EdgeInsets.only(left: 6, right: 8, top: 4, bottom: 4),
+ child: Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ Icon(
+ widget.selected ? Icons.check_circle : Icons.tag,
+ size: 16,
+ color: widget.selected
+ ? Theme.of(context).colorScheme.primary
+ : Theme.of(context).colorScheme.onSurface,
+ ),
+ const SizedBox(width: 6),
+ Text(
+ widget.tag,
+ style: TextStyle(
+ color: widget.selected
+ ? Theme.of(context).colorScheme.primary
+ : Theme.of(context).colorScheme.onSurface,
+ ),
+ ),
+ if (widget.suffix case Widget suffix) ...[
+ const SizedBox(width: 4),
+ suffix,
+ ],
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/stories/rolling_app_bar_animation.dart b/example/lib/stories/rolling_app_bar_animation.dart
new file mode 100644
index 0000000..055edc7
--- /dev/null
+++ b/example/lib/stories/rolling_app_bar_animation.dart
@@ -0,0 +1,99 @@
+import 'package:flutter/material.dart';
+import 'package:hyper_effects/hyper_effects.dart';
+
+class RollingAppBarAnimation extends StatefulWidget {
+ const RollingAppBarAnimation({super.key});
+
+ @override
+ State createState() => _RollingAppBarAnimationState();
+}
+
+class _RollingAppBarAnimationState extends State {
+ final TextEditingController searchController = TextEditingController();
+ bool showSearchView = false;
+
+ @override
+ void dispose() {
+ searchController.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: Container(
+ padding: const EdgeInsets.all(8),
+ margin: const EdgeInsets.all(32),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surface,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ clipBehavior: Clip.hardEdge,
+ child: Row(
+ children: [
+ Expanded(
+ child: TextField(
+ controller: searchController,
+ decoration: const InputDecoration(hintText: 'Search'),
+ onTap: () {
+ showSearchView = true;
+ setState(() {});
+ },
+ onTapOutside: (event) {
+ showSearchView = false;
+ setState(() {});
+ },
+ ),
+ ),
+ KeyedSubtree(
+ key: ValueKey(showSearchView),
+ child: showSearchView
+ ? TextButton(
+ child: const Text('Cancel'),
+ onPressed: () {
+ showSearchView = false;
+ setState(() {});
+ },
+ )
+ : const HomeButtonTools(),
+ )
+ .roll(
+ multiplier: 1.5,
+ slideInDirection:
+ showSearchView ? AxisDirection.right : AxisDirection.left,
+ slideOutDirection:
+ showSearchView ? AxisDirection.right : AxisDirection.left,
+ )
+ .clip(0)
+ .animate(
+ trigger: showSearchView,
+ duration: const Duration(milliseconds: 350),
+ curve: Curves.easeInOutQuart,
+ )
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class HomeButtonTools extends StatefulWidget {
+ const HomeButtonTools({super.key});
+
+ @override
+ State createState() => _HomeButtonToolsState();
+}
+
+class _HomeButtonToolsState extends State {
+ @override
+ Widget build(BuildContext context) {
+ return Row(
+ children: [
+ IconButton(onPressed: () {}, icon: const Icon(Icons.filter_alt)),
+ IconButton(onPressed: () {}, icon: const Icon(Icons.compare_arrows)),
+ IconButton(onPressed: () {}, icon: const Icon(Icons.add)),
+ IconButton(onPressed: () {}, icon: const Icon(Icons.settings)),
+ ],
+ );
+ }
+}
diff --git a/example/lib/stories/rolling_pictures_animation.dart b/example/lib/stories/rolling_pictures_animation.dart
new file mode 100644
index 0000000..edf0c42
--- /dev/null
+++ b/example/lib/stories/rolling_pictures_animation.dart
@@ -0,0 +1,52 @@
+import 'package:flutter/material.dart';
+import 'package:hyper_effects/hyper_effects.dart';
+
+class RollingWidgetAnimation extends StatefulWidget {
+ const RollingWidgetAnimation({super.key});
+
+ @override
+ State createState() => _RollingWidgetAnimationState();
+}
+
+class _RollingWidgetAnimationState extends State {
+ int counter = 0;
+
+ @override
+ Widget build(BuildContext context) {
+ return Center(
+ child: Column(
+ children: [
+ (counter % 9 == 8
+ ? null
+ : Image.asset(
+ 'assets/fashion/fashion_${counter % 9}.jpg',
+ key: ValueKey(counter),
+ height: 500 + (counter % 2 == 0 ? 0 : 100),
+ cacheHeight: 500 + (counter % 2 == 0 ? 0 : 100),
+ ))
+ .roll(
+ slideInDirection: AxisDirection.up,
+ slideOutDirection: AxisDirection.left,
+ )
+ .clip(0)
+ .animate(
+ trigger: counter,
+ curve: Curves.easeInOutQuart,
+ duration: const Duration(milliseconds: 500),
+ ),
+ Align(
+ alignment: Alignment.topCenter,
+ child: ElevatedButton(
+ onPressed: () {
+ setState(() {
+ counter++;
+ });
+ },
+ child: const Text('Roll'),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/example/lib/stories/scroll_wheel_blur.dart b/example/lib/stories/scroll_phase_blur.dart
similarity index 100%
rename from example/lib/stories/scroll_wheel_blur.dart
rename to example/lib/stories/scroll_phase_blur.dart
diff --git a/example/lib/stories/scroll_phase_transition.dart b/example/lib/stories/scroll_phase_slide.dart
similarity index 100%
rename from example/lib/stories/scroll_phase_transition.dart
rename to example/lib/stories/scroll_phase_slide.dart
diff --git a/example/lib/stories/shake_and_spring_animation.dart b/example/lib/stories/shake_and_spring_animation.dart
index 0e9ad45..b6b6f5a 100644
--- a/example/lib/stories/shake_and_spring_animation.dart
+++ b/example/lib/stories/shake_and_spring_animation.dart
@@ -24,7 +24,7 @@ class _SpringAnimationState extends State {
.shake()
.animate(
trigger: trigger,
- startImmediately: true,
+ startState: AnimationStartState.playImmediately,
delay: const Duration(seconds: 1),
repeat: -1,
playIf: () => !trigger,
diff --git a/example/lib/stories/success_card_animation.dart b/example/lib/stories/success_card_animation.dart
new file mode 100644
index 0000000..5327ff6
--- /dev/null
+++ b/example/lib/stories/success_card_animation.dart
@@ -0,0 +1,187 @@
+import 'dart:math';
+
+import 'package:flutter/material.dart';
+import 'package:hyper_effects/hyper_effects.dart';
+
+class SuccessCardAnimation extends StatefulWidget {
+ const SuccessCardAnimation({super.key});
+
+ @override
+ State createState() => _SuccessCardAnimationState();
+}
+
+class _SuccessCardAnimationState extends State {
+ bool isCompleted = false;
+
+ @override
+ Widget build(BuildContext context) {
+ return Column(
+ mainAxisSize: MainAxisSize.min,
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 350,
+ maxHeight: 200,
+ ),
+ child: MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ setState(() {
+ isCompleted = !isCompleted;
+ });
+ },
+ child: Padding(
+ padding: const EdgeInsets.all(12),
+ child: Container(
+ constraints: const BoxConstraints(minHeight: 148),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(16),
+ child: Stack(
+ alignment: Alignment.center,
+ fit: StackFit.expand,
+ children: [
+ Image.asset(
+ 'assets/fashion/fashion_0.jpg',
+ fit: BoxFit.cover,
+ ),
+ Positioned.fill(
+ child: Container(
+ decoration: const BoxDecoration(
+ color: Color(0x3A079455),
+ ),
+ alignment: Alignment.center,
+ child: const Icon(
+ Icons.check_circle,
+ size: 74,
+ )
+ .translateY(
+ isCompleted ? 0 : 100,
+ from: isCompleted ? 100 : 0,
+ )
+ .animate(
+ trigger: isCompleted,
+ startState:
+ AnimationStartState.useCurrentValues,
+ curve: !isCompleted
+ ? Curves.easeInBack
+ : Curves.easeOutBack,
+ duration: const Duration(
+ milliseconds: 400,
+ ),
+ ),
+ )
+ .opacity(
+ isCompleted ? 1 : 0,
+ from: isCompleted ? 0 : 1,
+ )
+ .animate(
+ trigger: isCompleted,
+ startState:
+ AnimationStartState.useCurrentValues,
+ curve: Curves.easeInOutSine,
+ duration: const Duration(
+ milliseconds: 400,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ConstrainedBox(
+ constraints: const BoxConstraints(
+ maxWidth: 350,
+ maxHeight: 200,
+ ),
+ child: MouseRegion(
+ cursor: SystemMouseCursors.click,
+ child: GestureDetector(
+ onTap: () {
+ setState(() {
+ isCompleted = !isCompleted;
+ });
+ },
+ child: Padding(
+ padding: const EdgeInsets.all(12),
+ child: Container(
+ constraints: const BoxConstraints(minHeight: 148),
+ decoration: BoxDecoration(
+ color: Colors.white,
+ borderRadius: BorderRadius.circular(16),
+ ),
+ child: ClipRRect(
+ borderRadius: BorderRadius.circular(16),
+ child: Stack(
+ alignment: Alignment.center,
+ fit: StackFit.expand,
+ children: [
+ Image.asset(
+ 'assets/fashion/fashion_0.jpg',
+ fit: BoxFit.cover,
+ ),
+ Positioned.fill(
+ child: Container(
+ decoration: const BoxDecoration(
+ color: Color(0x3A079455),
+ ),
+ alignment: Alignment.center,
+ child: const Icon(
+ Icons.check_circle,
+ size: 74,
+ )
+ .scale(isCompleted ? 1.5 : 0)
+ .rotate(isCompleted ? 15 * pi / 180 : 0)
+ .animate(
+ trigger: isCompleted,
+ curve: Curves.easeOutQuart,
+ duration:
+ const Duration(milliseconds: 350),
+ )
+ .scale(
+ isCompleted ? 1 / 1.5 : 1,
+ )
+ .rotate(
+ isCompleted ? -15 * pi / 180 : 0,
+ )
+ .animateAfter(
+ curve: Curves.easeOutBack,
+ delay: const Duration(milliseconds: 150),
+ duration:
+ const Duration(milliseconds: 300),
+ ))
+ // .opacity(
+ // isCompleted ? 1 : 0,
+ // from: isCompleted ? 0 : 1,
+ // )
+ // .animate(
+ // trigger: isCompleted,
+ // startState:
+ // AnimationStartState.useCurrentValues,
+ // curve: Curves.easeInOutSine,
+ // duration: const Duration(
+ // milliseconds: 400,
+ // ),
+ // ),
+ ),
+ ],
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ),
+ ],
+ );
+ }
+}
diff --git a/example/lib/stories/text_animation.dart b/example/lib/stories/text_animation.dart
index eec4b7c..b03b76b 100644
--- a/example/lib/stories/text_animation.dart
+++ b/example/lib/stories/text_animation.dart
@@ -5,6 +5,7 @@ import 'package:flutter/scheduler.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:hyper_effects/hyper_effects.dart';
import 'package:smooth_page_indicator/smooth_page_indicator.dart';
+import 'package:unicode_emojis/unicode_emojis.dart';
class TextAnimation extends StatefulWidget {
const TextAnimation({super.key});
@@ -78,6 +79,18 @@ class IPhone extends StatelessWidget {
}
}
+final String _allEmojis =
+ UnicodeEmojis.allEmojis.map((emoji) => emoji.emoji).join('');
+
+class EmojiTapeBuilder extends CharacterTapeBuilder {
+ @override
+ String get characters => _allEmojis;
+
+ @override
+ bool compare(String a, String b) =>
+ _allEmojis.contains(a) && _allEmojis.contains(b);
+}
+
class EmojiLine extends StatefulWidget {
const EmojiLine({super.key});
@@ -103,24 +116,29 @@ class _EmojiLineState extends State {
borderRadius: BorderRadius.circular(32),
),
child: Text(
- 'Hello ππππππ
ππ€£π₯²π₯ΉοΈππππππ Sexy',
+ trigger
+ ? 'World π§³πβοΈπ§΅πͺ‘πͺ’πͺπ§ΆππΆπ₯½π₯Όπ¦Ίππ𧣠Effect'
+ : 'Hello ππππππ
ππ€£π₯²π₯ΉοΈππππππ Sexy',
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryContainer,
),
)
.roll(
- 'World π§³πβοΈπ§΅πͺ‘πͺ’πͺπ§ΆππΆπ₯½π₯Όπ¦Ίππ𧣠Effect',
- tapeStrategy: const ConsistentSymbolTapeStrategy(4),
- tapeSlideDirection: TapeSlideDirection.alternating,
- staggerTapes: false,
+ tapeStrategy:
+ ConsistentSymbolTapeStrategy(4, characterTapeBuilders: {
+ EmojiTapeBuilder(),
+ }),
+ tapeSlideDirection: TextTapeSlideDirection.alternating,
+ staggerTapes: true,
tapeCurve: Curves.easeInOutBack,
widthCurve: Curves.easeOutQuart,
symbolDistanceMultiplier: 2,
+ staggerSoftness: 30,
+ // clipBehavior: Clip.none,
)
.animate(
trigger: trigger,
- reverse: true,
- duration: const Duration(milliseconds: 1500),
+ duration: const Duration(milliseconds: 2000),
),
),
);
@@ -148,7 +166,6 @@ class _TagLineState extends State {
'Build',
'Code',
];
- int lastTagLine = 0;
int tagLine = 0;
late Timer timer;
@@ -159,7 +176,6 @@ class _TagLineState extends State {
timer = Timer.periodic(
Duration(milliseconds: (1800 * timeDilation).toInt()), (timer) {
setState(() {
- lastTagLine = tagLine;
tagLine = (tagLine + 1) % tagLines.length;
});
});
@@ -179,7 +195,7 @@ class _TagLineState extends State {
Text(
'We help you',
style: GoogleFonts.robotoMono().copyWith(
- color: Theme.of(context).colorScheme.onBackground,
+ color: Theme.of(context).colorScheme.onSurface,
fontSize: 48,
),
strutStyle: const StrutStyle(
@@ -212,7 +228,7 @@ class _TagLineState extends State {
],
).createShader(rect),
child: Text(
- tagLines[lastTagLine],
+ tagLines[tagLine],
style: GoogleFonts.gloriaHallelujah().copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
@@ -220,9 +236,8 @@ class _TagLineState extends State {
),
)
.roll(
- tagLines[tagLine],
symbolDistanceMultiplier: 2,
- tapeSlideDirection: TapeSlideDirection.down,
+ tapeSlideDirection: TextTapeSlideDirection.down,
tapeCurve: Curves.easeInOutCubic,
widthCurve: Curves.easeOutCubic,
widthDuration: const Duration(milliseconds: 1000),
@@ -263,7 +278,6 @@ class _TranslationState extends State {
'Namaste',
'Salaam',
];
- int lastTranslation = 0;
int translation = 0;
late Timer timer;
@@ -275,7 +289,6 @@ class _TranslationState extends State {
timer = Timer.periodic(
Duration(milliseconds: (2000 * timeDilation).toInt()), (timer) {
setState(() {
- lastTranslation = translation;
translation = (translation + 1) % translations.length;
});
});
@@ -315,7 +328,7 @@ class _TranslationState extends State {
],
).createShader(rect),
child: Text(
- translations[lastTranslation],
+ translations[translation],
style: GoogleFonts.sacramento().copyWith(
color: Colors.white,
fontWeight: FontWeight.bold,
@@ -323,7 +336,6 @@ class _TranslationState extends State {
),
)
.roll(
- translations[translation],
symbolDistanceMultiplier: 2,
tapeCurve: Curves.easeInOutBack,
widthCurve: Curves.easeInOutQuart,
@@ -338,7 +350,7 @@ class _TranslationState extends State {
Text(
', Stranger',
style: GoogleFonts.sacramento().copyWith(
- color: Theme.of(context).colorScheme.onBackground,
+ color: Theme.of(context).colorScheme.onSurface,
fontSize: 56,
),
strutStyle: const StrutStyle(
@@ -361,7 +373,6 @@ class LikeButton extends StatefulWidget {
}
class _LikeButtonState extends State {
- int lastCounter = 19;
int counter = 19;
bool triggerShare = false;
int downloadIteration = 1;
@@ -378,7 +389,6 @@ class _LikeButtonState extends State {
child: InkWell(
onTap: () {
setState(() {
- lastCounter = counter;
counter++;
});
},
@@ -395,14 +405,15 @@ class _LikeButtonState extends State {
),
const SizedBox(width: 8),
Text(
- '${lastCounter}K',
+ '${counter}K',
style: GoogleFonts.robotoTextTheme()
.bodyMedium!
.copyWith(color: Colors.white, fontSize: 16),
)
.roll(
- '${counter}K',
- tapeStrategy: const AllSymbolsTapeStrategy(false),
+ tapeStrategy: const AllSymbolsTapeStrategy(
+ repeatCharacters: false,
+ ),
symbolDistanceMultiplier: 2,
clipBehavior: Clip.none,
tapeCurve: Curves.easeOutQuart,
@@ -453,15 +464,14 @@ class _LikeButtonState extends State {
),
const SizedBox(width: 8),
Text(
- 'Share',
+ triggerShare ? 'Thanks!' : 'Share',
style: GoogleFonts.robotoTextTheme()
.bodyMedium!
.copyWith(color: Colors.white, fontSize: 16),
)
.roll(
- 'Thanks!',
- tapeStrategy:
- const ConsistentSymbolTapeStrategy(0, true),
+ tapeStrategy: const ConsistentSymbolTapeStrategy(0,
+ repeatCharacters: true),
symbolDistanceMultiplier: 2,
clipBehavior: Clip.none,
tapeCurve:
@@ -513,13 +523,8 @@ class _LikeButtonState extends State {
.copyWith(color: Colors.white, fontSize: 16),
)
.roll(
- switch (downloadIteration) {
- 1 => 'Downloading',
- 2 => 'Downloaded',
- _ => 'Download',
- },
- tapeStrategy:
- const ConsistentSymbolTapeStrategy(0, false),
+ tapeStrategy: const ConsistentSymbolTapeStrategy(0,
+ repeatCharacters: false),
symbolDistanceMultiplier: 2,
clipBehavior: Clip.none,
tapeCurve: Curves.easeOutBack,
@@ -642,7 +647,6 @@ class _ColorPalettePageState extends State {
]
};
- int lastPage = 0;
int currentPage = 0;
final PageController _pageController = PageController();
@@ -694,15 +698,14 @@ class _ColorPalettePageState extends State {
colors: palettes.values.elementAt(currentPage),
).createShader(rect),
child: Text(
- palettes.keys.elementAt(lastPage).toUpperCase(),
+ palettes.keys.elementAt(currentPage).toUpperCase(),
style: const TextStyle(
fontWeight: FontWeight.w700, color: Colors.white),
)
.roll(
- palettes.keys.elementAt(currentPage).toUpperCase(),
staggerSoftness: 6,
reverseStaggerDirection: false,
- tapeSlideDirection: TapeSlideDirection.down,
+ tapeSlideDirection: TextTapeSlideDirection.down,
tapeCurve: Curves.easeOutBack,
widthCurve: Curves.easeOutQuart,
widthDuration: const Duration(milliseconds: 500),
@@ -731,7 +734,6 @@ class _ColorPalettePageState extends State {
itemCount: palettes.keys.length,
onPageChanged: (int page) {
setState(() {
- lastPage = currentPage;
currentPage = page;
});
},
diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj
index e1c62d4..df9b74f 100644
--- a/example/macos/Runner.xcodeproj/project.pbxproj
+++ b/example/macos/Runner.xcodeproj/project.pbxproj
@@ -21,14 +21,14 @@
/* End PBXAggregateTarget section */
/* Begin PBXBuildFile section */
+ 233C6FD519D5A479DBC4A7B3 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1B2C434BED57CE763B8D88B4 /* Pods_RunnerTests.framework */; };
331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };
335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
- 9749B080EE3903CE36CD9B9F /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF66A7423E27B1E1C2B7DF2E /* Pods_Runner.framework */; };
- BEEE65E5C6D9898EDDD2C219 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1A215E138480AD76222C3A2F /* Pods_RunnerTests.framework */; };
+ F2445E0FBADA22DBAECF88BF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24E95C664D0B1B1E9C8D1567 /* Pods_Runner.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -62,14 +62,15 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 02C1D53FE00F265778E04051 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
- 0A9CAE6517C9B0AEB712B7E8 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
- 1A215E138480AD76222C3A2F /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 05D7AD55499634497DDAAEE2 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ 0AEA63D43EC920AC96E17696 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
+ 1B2C434BED57CE763B8D88B4 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 24E95C664D0B1B1E9C8D1567 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
- 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10ED2044A3C60003C045 /* hyper_effects_demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = hyper_effects_demo.app; sourceTree = BUILT_PRODUCTS_DIR; };
33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
@@ -81,13 +82,12 @@
33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
- 713C55CFD54062B2ACD29F8E /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ 6AF86FC0D986B41DFB12F321 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
- 8026674D67C5F4C67964DC92 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
- 81981A169A98233BCEDE34B9 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
- A01D35E74C0CF549EBD91FDB /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
- DF66A7423E27B1E1C2B7DF2E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ A02F36C029429126D3B2C5AA /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
+ A66162B928096445514EFC91 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ FF533E0B3F902C87057D3406 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -95,7 +95,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- BEEE65E5C6D9898EDDD2C219 /* Pods_RunnerTests.framework in Frameworks */,
+ 233C6FD519D5A479DBC4A7B3 /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -103,13 +103,27 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 9749B080EE3903CE36CD9B9F /* Pods_Runner.framework in Frameworks */,
+ F2445E0FBADA22DBAECF88BF /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
+ 263529EFD77750E6A2E03F88 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ A66162B928096445514EFC91 /* Pods-Runner.debug.xcconfig */,
+ 6AF86FC0D986B41DFB12F321 /* Pods-Runner.release.xcconfig */,
+ FF533E0B3F902C87057D3406 /* Pods-Runner.profile.xcconfig */,
+ 05D7AD55499634497DDAAEE2 /* Pods-RunnerTests.debug.xcconfig */,
+ A02F36C029429126D3B2C5AA /* Pods-RunnerTests.release.xcconfig */,
+ 0AEA63D43EC920AC96E17696 /* Pods-RunnerTests.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
331C80D6294CF71000263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
@@ -137,14 +151,14 @@
331C80D6294CF71000263BE5 /* RunnerTests */,
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
- 4259EDDF2DD4F19F2F705E7E /* Pods */,
+ 263529EFD77750E6A2E03F88 /* Pods */,
);
sourceTree = "";
};
33CC10EE2044A3C60003C045 /* Products */ = {
isa = PBXGroup;
children = (
- 33CC10ED2044A3C60003C045 /* example.app */,
+ 33CC10ED2044A3C60003C045 /* hyper_effects_demo.app */,
331C80D5294CF71000263BE5 /* RunnerTests.xctest */,
);
name = Products;
@@ -185,25 +199,11 @@
path = Runner;
sourceTree = "";
};
- 4259EDDF2DD4F19F2F705E7E /* Pods */ = {
- isa = PBXGroup;
- children = (
- 02C1D53FE00F265778E04051 /* Pods-Runner.debug.xcconfig */,
- 0A9CAE6517C9B0AEB712B7E8 /* Pods-Runner.release.xcconfig */,
- 8026674D67C5F4C67964DC92 /* Pods-Runner.profile.xcconfig */,
- A01D35E74C0CF549EBD91FDB /* Pods-RunnerTests.debug.xcconfig */,
- 713C55CFD54062B2ACD29F8E /* Pods-RunnerTests.release.xcconfig */,
- 81981A169A98233BCEDE34B9 /* Pods-RunnerTests.profile.xcconfig */,
- );
- name = Pods;
- path = Pods;
- sourceTree = "";
- };
D73912EC22F37F3D000D13A0 /* Frameworks */ = {
isa = PBXGroup;
children = (
- DF66A7423E27B1E1C2B7DF2E /* Pods_Runner.framework */,
- 1A215E138480AD76222C3A2F /* Pods_RunnerTests.framework */,
+ 24E95C664D0B1B1E9C8D1567 /* Pods_Runner.framework */,
+ 1B2C434BED57CE763B8D88B4 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -215,7 +215,7 @@
isa = PBXNativeTarget;
buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
- F4B967E491B126A3E7A6DAC9 /* [CP] Check Pods Manifest.lock */,
+ 00AD03243451643F0598D10C /* [CP] Check Pods Manifest.lock */,
331C80D1294CF70F00263BE5 /* Sources */,
331C80D2294CF70F00263BE5 /* Frameworks */,
331C80D3294CF70F00263BE5 /* Resources */,
@@ -234,13 +234,13 @@
isa = PBXNativeTarget;
buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- 08FBC521EC148D3FE9645785 /* [CP] Check Pods Manifest.lock */,
+ FFDE6EBFA831BB29157AA743 /* [CP] Check Pods Manifest.lock */,
33CC10E92044A3C60003C045 /* Sources */,
33CC10EA2044A3C60003C045 /* Frameworks */,
33CC10EB2044A3C60003C045 /* Resources */,
33CC110E2044A8840003C045 /* Bundle Framework */,
3399D490228B24CF009A79C7 /* ShellScript */,
- B99A704086D4B9C28A5DB143 /* [CP] Embed Pods Frameworks */,
+ DC25671DD086C637864AD21C /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
@@ -249,7 +249,7 @@
);
name = Runner;
productName = Runner;
- productReference = 33CC10ED2044A3C60003C045 /* example.app */;
+ productReference = 33CC10ED2044A3C60003C045 /* hyper_effects_demo.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
@@ -258,8 +258,9 @@
33CC10E52044A3C60003C045 /* Project object */ = {
isa = PBXProject;
attributes = {
+ BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 0920;
- LastUpgradeCheck = 1430;
+ LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C80D4294CF70F00263BE5 = {
@@ -322,7 +323,7 @@
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
- 08FBC521EC148D3FE9645785 /* [CP] Check Pods Manifest.lock */ = {
+ 00AD03243451643F0598D10C /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -337,7 +338,7 @@
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -382,7 +383,7 @@
shellPath = /bin/sh;
shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
};
- B99A704086D4B9C28A5DB143 /* [CP] Embed Pods Frameworks */ = {
+ DC25671DD086C637864AD21C /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -399,7 +400,7 @@
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
- F4B967E491B126A3E7A6DAC9 /* [CP] Check Pods Manifest.lock */ = {
+ FFDE6EBFA831BB29157AA743 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -414,7 +415,7 @@
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -472,46 +473,46 @@
/* Begin XCBuildConfiguration section */
331C80DB294CF71000263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = A01D35E74C0CF549EBD91FDB /* Pods-RunnerTests.debug.xcconfig */;
+ baseConfigurationReference = 05D7AD55499634497DDAAEE2 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.hyperEffectsDemo.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hyper_effects_demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hyper_effects_demo";
};
name = Debug;
};
331C80DC294CF71000263BE5 /* Release */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 713C55CFD54062B2ACD29F8E /* Pods-RunnerTests.release.xcconfig */;
+ baseConfigurationReference = A02F36C029429126D3B2C5AA /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.hyperEffectsDemo.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hyper_effects_demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hyper_effects_demo";
};
name = Release;
};
331C80DD294CF71000263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
- baseConfigurationReference = 81981A169A98233BCEDE34B9 /* Pods-RunnerTests.profile.xcconfig */;
+ baseConfigurationReference = 0AEA63D43EC920AC96E17696 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.example.hyperEffectsDemo.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
- TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/hyper_effects_demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/hyper_effects_demo";
};
name = Profile;
};
@@ -520,6 +521,7 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -543,9 +545,11 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -593,6 +597,7 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -616,9 +621,11 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -646,6 +653,7 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
@@ -669,9 +677,11 @@
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
+ DEAD_CODE_STRIPPING = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 397f3d3..89451a0 100644
--- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
@@ -31,7 +31,7 @@
@@ -65,7 +65,7 @@
@@ -82,7 +82,7 @@
diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift
index d53ef64..b3c1761 100644
--- a/example/macos/Runner/AppDelegate.swift
+++ b/example/macos/Runner/AppDelegate.swift
@@ -1,9 +1,13 @@
import Cocoa
import FlutterMacOS
-@NSApplicationMain
+@main
class AppDelegate: FlutterAppDelegate {
override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}
+
+ override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {
+ return true
+ }
}
diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/example/macos/Runner/Configs/AppInfo.xcconfig
index dda192b..d9e0e80 100644
--- a/example/macos/Runner/Configs/AppInfo.xcconfig
+++ b/example/macos/Runner/Configs/AppInfo.xcconfig
@@ -5,10 +5,10 @@
// 'flutter create' template.
// The application's name. By default this is also the title of the Flutter window.
-PRODUCT_NAME = example
+PRODUCT_NAME = hyper_effects_demo
// The application's bundle identifier
-PRODUCT_BUNDLE_IDENTIFIER = com.example.example
+PRODUCT_BUNDLE_IDENTIFIER = com.example.hyperEffectsDemo
// The copyright displayed in application information
-PRODUCT_COPYRIGHT = Copyright Β© 2023 com.example. All rights reserved.
+PRODUCT_COPYRIGHT = Copyright Β© 2024 com.example. All rights reserved.
diff --git a/example/macos/RunnerTests/RunnerTests.swift b/example/macos/RunnerTests/RunnerTests.swift
index 5418c9f..61f3bd1 100644
--- a/example/macos/RunnerTests/RunnerTests.swift
+++ b/example/macos/RunnerTests/RunnerTests.swift
@@ -1,5 +1,5 @@
-import FlutterMacOS
import Cocoa
+import FlutterMacOS
import XCTest
class RunnerTests: XCTestCase {
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 5ef917a..b07efbc 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -10,17 +10,18 @@ environment:
dependencies:
flutter:
sdk: flutter
- adaptive_theme: ^3.4.1
- flutter_box_transform: ^0.4.2
+ adaptive_theme: ^3.6.0
+ flutter_box_transform: ^0.4.6
hyper_effects:
path: ../
- google_fonts: ^6.1.0
- smooth_page_indicator: ^1.1.0
+ google_fonts: ^6.2.1
+ unicode_emojis: ^0.4.0
+ smooth_page_indicator: ^1.2.0+3
dev_dependencies:
flutter_test:
sdk: flutter
- flutter_lints: ^3.0.1
+ flutter_lints: ^5.0.0
flutter:
uses-material-design: true
diff --git a/example/web/favicon.png b/example/web/favicon.png
index 8aaa46a..fe2faa5 100644
Binary files a/example/web/favicon.png and b/example/web/favicon.png differ
diff --git a/example/web/flutter_bootstrap.js b/example/web/flutter_bootstrap.js
new file mode 100644
index 0000000..775f975
--- /dev/null
+++ b/example/web/flutter_bootstrap.js
@@ -0,0 +1,14 @@
+{{flutter_js}}
+{{flutter_build_config}}
+
+// Customize the app initialization process
+_flutter.loader.load({
+ onEntrypointLoaded: async function(engineInitializer) {
+ const appRunner = await engineInitializer.initializeEngine({
+ useColorEmoji: true
+ });
+
+ await appRunner.runApp();
+ }
+
+});
diff --git a/example/web/icons/Icon-192.png b/example/web/icons/Icon-192.png
index b749bfe..0590b43 100644
Binary files a/example/web/icons/Icon-192.png and b/example/web/icons/Icon-192.png differ
diff --git a/example/web/icons/Icon-512.png b/example/web/icons/Icon-512.png
index 88cfd48..c714458 100644
Binary files a/example/web/icons/Icon-512.png and b/example/web/icons/Icon-512.png differ
diff --git a/example/web/icons/Icon-maskable-192.png b/example/web/icons/Icon-maskable-192.png
index eb9b4d7..88c3124 100644
Binary files a/example/web/icons/Icon-maskable-192.png and b/example/web/icons/Icon-maskable-192.png differ
diff --git a/example/web/icons/Icon-maskable-512.png b/example/web/icons/Icon-maskable-512.png
index d69c566..2d7ea8e 100644
Binary files a/example/web/icons/Icon-maskable-512.png and b/example/web/icons/Icon-maskable-512.png differ
diff --git a/example/web/index.html b/example/web/index.html
index 2e043ca..c7c2880 100644
--- a/example/web/index.html
+++ b/example/web/index.html
@@ -12,39 +12,16 @@
-
+
-
+
-
- example
-
-
-
-
-
+