Hyper Effects는 최소한의 코드로 놀라운 시각 효과와 부드러운 애니메이션을 만들 수 있도록 해주는 Flutter/Dart 패키지입니다. 간결함과 성능을 중시하여 글로우, 블러, 리플, 변형 애니메이션 등 다양한 사전 구축된 효과를 제공합니다. 동적 피드백과 정교한 전환을 통해 사용자 인터페이스를 강화하는 데 이상적이며, 모든 주요 플랫폼에서 Flutter 앱에 원활하게 통합됩니다. 이 패키지는 봉쇄 코드를 줄여 개발 생산성을 높이면서도 높은 렌더링 효율성을 유지합니다.
사용 사례
인터랙티브 UI 피드백 추가
애니메이션 스플래시 화면 만들기
버튼 및 제스처 반응 향상
매력적인 온보딩 경험 설계하기
현대적인 대시보드 시각화 구축
목록 항목 및 카드 애니메이션
주요 기능
복잡한 효과를 위한 최소 코드
사전 구축된 애니메이션 프리셋
크로스 플랫폼 호환성
고성능 렌더링
기존 위젯과의 간편한 통합
사용자 정의 가능한 효과 매개변수
적합한 대상
빠른 프로토타이핑을 원하는 UI/UX 디자이너
인터랙티브 앱을 구축하는 Flutter 개발자
정교한 앱 미학에 집중하는 팀
경량 애니메이션을 필요로 하는 프로젝트
다중 플랫폼을 대상으로 하는 개발자
고려 사항
패키지 크기가 약간 증가할 수 있습니다
기본 애니메이션 개념에 대한 이해가 필요합니다
과도하게 사용할 경우 저사양 기기에서 성능에 영향을 미침
제공된 사전 설정 외의 맞춤 설정이 제한적입니다
시각적 혼잡을 피하기 위해 선택적으로 사용하는 것이 가장 좋습니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
Hyper Effects
Hyper Effects is a Flutter package
inspired from SwiftUI's syntax to create beautiful effects in Flutter. With just a few lines of code after your
widget, you can add any number of effects and transitions with ease.
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:
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.
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.
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 any effect, you need to use the animate method on the widget like so:
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 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 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.
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.
Despite having really nice default values, the API of course allows you to customize the animation to your liking. You
can do so by your own duration and curve.
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
be triggered again.
duration: The duration of the animation.
curve: The curve of the animation.
onEnd: A callback that is called when the animation ends.
repeat: Determines how many times the animation should be repeated.
reverse: A boolean property that determines whether the animation should be reversed after each repetition.
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.
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
If you want to trigger an animation immediately, you can use the oneShot function which triggers a chain of effects
immediately without a trigger parameter.
This function is useful when you want to start an animation as soon as the widget is built, without waiting for a
specific trigger to change. The animation will never play again after it ends.
Here's an example of how to use the oneShot function:
@overrideWidgetbuild(BuildContext context) {
returnContainer(
color: Colors.blue,
).slideInFromBottom()
.oneShot(
// All the normal parameters inside of .animate() but without the trigger and startState parameters.
);
}
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 simple sequence of animations where one animation starts after the previous one
ends.
Here's an example of how to use the animateAfter function:
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. In
other words, each individual Hyper Effects AnimationController in the descendent widget tree will be reset to its
initial state.
Delayed Animations
The delay parameter is used in the animate, animateAfter and oneShot methods. This parameter allows you to
specify a delay
before the animation starts.
The delay is specified as a Duration. Here's an example of how to use the delay parameter:
@overrideWidgetbuild(BuildContext context) {
returnContainer(
color: Colors.blue,
)
.opacity(myCondition ? 0 : 1)
.animate(
trigger: myCondition,
delay: constDuration(seconds: 1), // 1 second delay before the animation starts after the trigger changes.
);
}
In this example, the opacity animation will start 1 second after the myCondition trigger changes.
Repeat Animations
The repeat parameter is used in the animate, animateAfter, and oneShot functions.
This parameter allows you to specify how many times the animation will be repeated.
The repeat parameter is an integer, where 0 (default) means the animation will only play once and not repeat.
Any positive integer specifies the number of times the animation will repeat.
If the repeat parameter is set to -1, the animation will repeat indefinitely.
Here's an example of how to use the repeat parameter:
@overrideWidgetbuild(BuildContext context) {
returnContainer(
color: Colors.blue,
)
.opacity(myCondition ? 0 : 1)
.animate(
trigger: myCondition,
repeat: 3, // The animation will repeat 3 times
);
}
Note that if the reverse parameter is set to true, the repetition counter will count for each animation direction.
For example, if the repeat parameter is set to 3 and the reverse parameter is set to true, the animation will
play only 3 times, one of which is the reverse animation.
forwards -> backwards -> forwards -> done.
Rolling Text
The Rolling Text feature in Hyper Effects allows you to create a rolling animation from one text to another. Each
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:
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:
The Rolling Text feature provides several options for customization:
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: 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.
When myTrigger changes, the text will roll from 'Hello' to 'World' or vice versa.
The animate method also accepts duration and curve parameters that allow you to customize the animation's duration
and easing curve.
Please note that if you specify a curve that goes outside the bounds of 0 and 1 like Curves.elasticIn/Out or
Curves.easeIn/OutBack, the UI will crash with a warning that the width cannot be negative. To resolve this,
simply specify a custom curve for the width. You can do so like this:
Transitions are a special type of effect that allows you to control how your widgets look based on some continuous
value. For example, you can control the opacity of a widget based on its position in a scroll view or based on the
pointer position.
Transitions use a builder pattern to provide you with an event property that gives you active information about the
state of a transition for your widget. You can use this information to control how your widget looks, and it will
animate automatically based on the effects you use inside.
Scroll Transitions
Scroll transitions are transitions that are based on the position of a widget in a scroll view. You can use the
scrollTransition extension on any widget to add a scroll transition to it.
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.
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
ScrollPhase.
With that in mind, despite the fact that you can use phase to animte your widgets easily with the extensions, for
other properties likescreenOffsetFraction, you will need to use the manual Effects API to animate them.
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
transition should output its values in its builder.
In the above example, the container will move half the distance of the pointer's movement in the x and y axis. The
fractional parameter is set to true, which means that the widget will move by multiplying its size by the value
provided. If fractional is set to false, the widget will move by the value provided directly in pixels.
value is a double parameter that represents the distance of the pointer device from the origin.
A value of zero is when the pointer device is at the origin.
A value of 1 or -1 is when the pointer device is at the farthest point from the origin.
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.
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.
isInsideBounds is a boolean parameter that decides whether the pointer is inside the bounds of the widget or not.
position is the raw position of the pointer. It's the position of the pointer device from the top left corner of the
target. The target depends on the configurations below.
Pointer Transition Properties
When using the pointer transition, you can provide multiple parameters to control its behavior, which is reflected back
in the event provided to the builder.
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.
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
false, the pointer position will be calculated from the top left corner of the widget.
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
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
child back to a value of zero when the pointer device is outside the bounds of the widget. transitionBetweenBounds
decides whether the transition should animate or not.
curve is a Curve parameter that determines the curve of the internal when transitioning between bounds animation.
duration is a Duration parameter that determines the duration of the internal when transitioning between bounds.
Caution
Like the scroll transition, be careful when using value and valueOffset as it can be a bit tricky to use. The
pointer transition internally uses the value property to internally lerp between values. This means that if you use
other properties and expect them to transition perfectly, you may get unexpected results sometimes.
Feel free to join our Discord server for any inquiries or support: https://discord.gg/yrahEhCqTJ
License
BSD 3-ClauseLicenseCopyright (c) 2023, HyperdesignedRedistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributionsin binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.