-
Notifications
You must be signed in to change notification settings - Fork 3.8k
Introduce and migrate to DualTransitionBuilder #160
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
b68e3be
Introduce and migrate to DualTransitionBuilder
goderbauer 619c78e
++
goderbauer f4aa3a5
++
goderbauer 6cf9e6a
++
goderbauer 485e8c9
format
goderbauer 3290086
review comments
goderbauer 3e8f48b
add state tests for transitions
goderbauer b2223d0
format
goderbauer 3684bea
more tests and an optimization
goderbauer 0fc939a
added missing file
goderbauer 05d5fcd
self review
goderbauer c2f4513
review
goderbauer File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
packages/animations/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
| <plist version="1.0"> | ||
| <dict> | ||
| <key>IDEDidComputeMac32BitWarning</key> | ||
| <true/> | ||
| </dict> | ||
| </plist> |
200 changes: 200 additions & 0 deletions
200
packages/animations/lib/src/dual_transition_builder.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| // Copyright 2020 The Flutter Authors. All rights reserved. | ||
| // Use of this source code is governed by a BSD-style license that can be | ||
| // found in the LICENSE file. | ||
|
|
||
| import 'package:flutter/widgets.dart'; | ||
|
|
||
| /// Builder callback used by [DualTransitionBuilder]. | ||
| /// | ||
| /// The builder is expected to return a transition powered by the provided | ||
| /// `animation` and wrapping the provided `child`. | ||
| /// | ||
| /// The `animation` provided to the builder always runs forward from 0.0 to 1.0. | ||
| typedef TransitionBuilder = Widget Function( | ||
| BuildContext context, | ||
| Animation<double> animation, | ||
| Widget child, | ||
| ); | ||
|
|
||
| /// A transition builder that animates its [child] based on the | ||
| /// [AnimationStatus] of the provided [animation]. | ||
| /// | ||
| /// This widget can be used to specify different enter and exit transitions for | ||
| /// a [child]. | ||
| /// | ||
| /// While the [animation] runs forward, the [child] is animated according to | ||
| /// [forwardBuilder] and while the [animation] is running in reverse, it is | ||
| /// animated according to [reverseBuilder]. | ||
| /// | ||
| /// Using this builder allows the widget tree to maintain its shape by nesting | ||
| /// the enter and exit transitions. This ensures that no state information of | ||
| /// any descendant widget is lost when the transition starts or completes. | ||
| class DualTransitionBuilder extends StatefulWidget { | ||
| /// Creates a [DualTransitionBuilder]. | ||
| /// | ||
| /// The [animation], [forwardBuilder], and [reverseBuilder] arguments are | ||
| /// required and must not be null. | ||
| const DualTransitionBuilder({ | ||
| Key key, | ||
| @required this.animation, | ||
| @required this.forwardBuilder, | ||
| @required this.reverseBuilder, | ||
| this.child, | ||
| }) : assert(animation != null), | ||
| assert(forwardBuilder != null), | ||
| assert(reverseBuilder != null), | ||
| super(key: key); | ||
|
|
||
| /// The animation that drives the [child]'s transition. | ||
| /// | ||
| /// When this animation runs forward, the [child] transitions as specified by | ||
| /// [forwardBuilder]. When it runs in reverse, the child transitions according | ||
| /// to [reverseBuilder]. | ||
| final Animation<double> animation; | ||
|
|
||
| /// A builder for the transition that makes [child] appear on screen. | ||
| /// | ||
| /// The [child] should be fully visible when the provided `animation` reaches | ||
| /// 1.0. | ||
| /// | ||
| /// The `animation` provided to this builder is running forward from 0.0 to | ||
| /// 1.0 when [animation] runs _forward_. When [animation] runs in reverse, | ||
| /// the given `animation` is set to [kAlwaysCompleteAnimation]. | ||
| /// | ||
| /// See also: | ||
| /// | ||
| /// * [reverseBuilder], which builds the transition for making the [child] | ||
| /// disappear from the screen. | ||
| final TransitionBuilder forwardBuilder; | ||
|
|
||
| /// A builder for a transition that makes [child] disappear from the screen. | ||
| /// | ||
| /// The [child] should be fully invisible when the provided `animation` | ||
| /// reaches 1.0. | ||
| /// | ||
| /// The `animation` provided to this builder is running forward from 0.0 to | ||
| /// 1.0 when [animation] runs in _reverse_. When [animation] runs forward, | ||
| /// the given `animation` is set to [kAlwaysDismissedAnimation]. | ||
| /// | ||
| /// See also: | ||
| /// | ||
| /// * [forwardBuilder], which builds the transition for making the [child] | ||
| /// appear on screen. | ||
| final TransitionBuilder reverseBuilder; | ||
|
|
||
| /// The widget below this [DualTransitionBuilder] in the tree. | ||
| /// | ||
| /// This child widget will be wrapped by the transitions built by | ||
| /// [forwardBuilder] and [reverseBuilder]. | ||
| final Widget child; | ||
|
|
||
| @override | ||
| State<DualTransitionBuilder> createState() => _DualTransitionBuilderState(); | ||
| } | ||
|
|
||
| class _DualTransitionBuilderState extends State<DualTransitionBuilder> { | ||
| AnimationStatus _effectiveAnimationStatus; | ||
| final ProxyAnimation _forwardAnimation = ProxyAnimation(); | ||
| final ProxyAnimation _reverseAnimation = ProxyAnimation(); | ||
|
|
||
| @override | ||
| void initState() { | ||
| super.initState(); | ||
| _effectiveAnimationStatus = widget.animation.status; | ||
| widget.animation.addStatusListener(_animationListener); | ||
| _updateAnimations(); | ||
| } | ||
|
|
||
| void _animationListener(AnimationStatus animationStatus) { | ||
| final AnimationStatus oldEffective = _effectiveAnimationStatus; | ||
| _effectiveAnimationStatus = _calculateEffectiveAnimationStatus( | ||
| lastEffective: _effectiveAnimationStatus, | ||
| current: animationStatus, | ||
| ); | ||
| if (oldEffective != _effectiveAnimationStatus) { | ||
| _updateAnimations(); | ||
| } | ||
| } | ||
|
|
||
| @override | ||
| void didUpdateWidget(DualTransitionBuilder oldWidget) { | ||
| super.didUpdateWidget(oldWidget); | ||
| if (oldWidget.animation != widget.animation) { | ||
| oldWidget.animation.removeStatusListener(_animationListener); | ||
| widget.animation.addStatusListener(_animationListener); | ||
| _animationListener(widget.animation.status); | ||
| } | ||
| } | ||
|
|
||
| // When a transition is interrupted midway we just want to play the ongoing | ||
| // animation in reverse. Switching to the actual reverse transition would | ||
| // yield a disjoint experience since the forward and reverse transitions are | ||
| // very different. | ||
| AnimationStatus _calculateEffectiveAnimationStatus({ | ||
| @required AnimationStatus lastEffective, | ||
| @required AnimationStatus current, | ||
| }) { | ||
| assert(current != null); | ||
| assert(lastEffective != null); | ||
| switch (current) { | ||
| case AnimationStatus.dismissed: | ||
| case AnimationStatus.completed: | ||
| return current; | ||
| case AnimationStatus.forward: | ||
| switch (lastEffective) { | ||
| case AnimationStatus.dismissed: | ||
| case AnimationStatus.completed: | ||
| case AnimationStatus.forward: | ||
| return current; | ||
| case AnimationStatus.reverse: | ||
| return lastEffective; | ||
| } | ||
| break; | ||
| case AnimationStatus.reverse: | ||
| switch (lastEffective) { | ||
| case AnimationStatus.dismissed: | ||
| case AnimationStatus.completed: | ||
| case AnimationStatus.reverse: | ||
| return current; | ||
| case AnimationStatus.forward: | ||
| return lastEffective; | ||
| } | ||
| break; | ||
| } | ||
| return null; // unreachable | ||
| } | ||
|
|
||
| void _updateAnimations() { | ||
| switch (_effectiveAnimationStatus) { | ||
| case AnimationStatus.dismissed: | ||
| case AnimationStatus.forward: | ||
| _forwardAnimation.parent = widget.animation; | ||
| _reverseAnimation.parent = kAlwaysDismissedAnimation; | ||
| break; | ||
| case AnimationStatus.reverse: | ||
| case AnimationStatus.completed: | ||
| _forwardAnimation.parent = kAlwaysCompleteAnimation; | ||
| _reverseAnimation.parent = ReverseAnimation(widget.animation); | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| @override | ||
| void dispose() { | ||
| widget.animation.removeStatusListener(_animationListener); | ||
| super.dispose(); | ||
| } | ||
|
|
||
| @override | ||
| Widget build(BuildContext context) { | ||
| return widget.forwardBuilder( | ||
| context, | ||
| _forwardAnimation, | ||
| widget.reverseBuilder( | ||
| context, | ||
| _reverseAnimation, | ||
| widget.child, | ||
| ), | ||
| ); | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be a small detail, but does the year on this matter? I know the
flutter/flutterproject consolidated the years to be the same year a few months back.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We don't have rules established for this repository yet, so using the current year should be fine until we decide to unify.