-
Notifications
You must be signed in to change notification settings - Fork 3k
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
Adds animations package with open container transition #30
Conversation
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.
I'm only halfway through this. So far, just trivial comments to mark my progress :-).
/// Signature for a function that creates a [Widget] to be used within an | ||
/// [OpenContainer]. | ||
/// | ||
/// The `action` callback provided to [OpenContainer.closedBuilder] can be used |
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.
openBuilder?
/// The `action` callback provided to [OpenContainer.closedBuilder] can be used | ||
/// to open the container. The `action` callback provided to | ||
/// [OpenContainer.closedBuilder] can be used to close the container again. | ||
typedef OpenContainerChildBuilder = Widget Function( |
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.
Maybe just call this OpenContainerBuilder? That would forgo any discussion about if what was being built was actually the container's child, or a descendant.
VoidCallback action, | ||
); | ||
|
||
/// Container, that grows to fill the screen to reveal new content when tapped. |
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.
A container that grows...
/// Container, that grows to fill the screen to reveal new content when tapped. | ||
/// | ||
/// While the container is closed, it shows the [Widget] returned by | ||
/// [closedBuilder]. When the container is tapped the container opens: It grows |
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.
When the container is tapped it grows
@override | ||
Widget build(BuildContext context) { | ||
return _Hideable( | ||
key: _hidableKey, |
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.
Consistency is key :-) _hideable
Size get placeholder => _placeholder; | ||
Size _placeholder; | ||
set placeholder(Size value) { | ||
if (_placeholder == value) { |
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.
Assert value != null here?
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.
null is totally ok here, it means "don't use the placeholder" as per doc comment.
|
||
class _HideableState extends State<_Hideable> { | ||
/// When non-null the child is replaced by a [SizedBox] of the set size. | ||
Size get placeholder => _placeholder; |
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.
This property sounds like a widget value, maybe placeholderSize?
/// | ||
/// The value of this property is ignored when [placeholder] is non-null (i.e. | ||
/// [isInTree] returns false). | ||
bool get visible => _visible; |
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.
Maybe isVisible because bool?
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.
Just small stuff here. The only things that worried me (a little) was the shape of the underlying material when the animation ended, and the possibility that the entire tree might have been destroyed by the time the postFrameCallback runs.
} | ||
|
||
@override | ||
Duration get transitionDuration => const Duration(milliseconds: 300); |
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.
maybe this should be a parameter
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.
Added.
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.
Thanks for the review. Addressed all comments. PTAL
} | ||
|
||
@override | ||
Duration get transitionDuration => const Duration(milliseconds: 300); |
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.
Added.
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.
This looks good, even if it's just v0. Looking forward to trying it out.
LGTM
/// See also: | ||
/// | ||
/// * [Material.elevation], which is used to implement this property. | ||
final double closedElevation; |
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.
Maybe open and closed elevation parameters should be Animatable<double>
, so instead of
closedElevation: 0,
openElevation: 4,
You'd write
elevation: Tween<double>(begin: 0, end: 4),
The advantage is that you the developer could define an arbitrary elevation change (could be defined with a TweenSequence for example). The disadvantage is that it's not as clear: is "begin" open or closed?.
The open and closed shape properties could be handled similarly.
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.
Talking this over, we decided to not add more configurability to this for now.
/// `action` callback that is provided to the [closedBuilder]. | ||
final bool tappable; | ||
|
||
/// The time it will take to animate the container from its closed to its |
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.
Maybe provide open and closed durations?
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.
The underlying ModalRoute would have to be extended for this to support different durations.
/// to its original size while the widget returned by [openBuilder] is faded out | ||
/// and the widget returned by [openBuilder] is faded back in. | ||
/// | ||
/// By default, the container is in the closed state. |
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.
If I'm understanding things correctly, during the transition the widgets returned by the open and closed builders exist at the same time. Do we have to warn developers that these widgets cannot include the same global key?
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.
Added a warning.
final Rect rect = _insetsTween.evaluate(curvedAnimation); | ||
final Size size = _sizeTween.evaluate(curvedAnimation); | ||
|
||
return Container( |
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.
Could be a Padding widget
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.
Changed.
final Size size = _sizeTween.evaluate(curvedAnimation); | ||
|
||
return Container( | ||
padding: EdgeInsets.fromLTRB( |
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.
I wonder why we don't have EdgeInsets.fromRect()
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.
That seems like a good addition. Filed flutter/flutter#45576.
height: size.height, | ||
child: Material( | ||
clipBehavior: Clip.antiAlias, | ||
animationDuration: Duration.zero, |
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.
Maybe we need a Material.noImpliciAnimation() constructor - except with a better name.
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.
Yes, we do! flutter/flutter#45577
@@ -0,0 +1,15 @@ | |||
# Fancy pre-built Animations for Flutter |
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.
Eventually we should up with a less fanciful (ha) description of this package. Here and elsewhere.
matching: find.byType(Material), | ||
), | ||
); | ||
final Material srcMaterial = srcMaterialElement.widget; |
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.
Not sure why we don't use tester.widget here. Wouldn't this work?
final Material material = tester.widget<Material>(
find.ancestor(
of: find.text('Closed'),
matching: find.byType(Material),
),
);
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 could do that here. But further down I am checking how the Widget configuration of this element is changing over time and it this makes the test more symmetric.
* Fix gradient regressions, OoO support for use/gradients - Defer resolution to build and assert resolution doesn't happen during parsing. - Fix regressions introduced in the last commit around userSpaceOnUse with transforms for linear gradients, and regressions around radial gradients in general. - Tests Things that are still wrong: - I noticed while working on flutter_svg's "devil" SVG test that blends are not being accurately applied to individual paths. I'll try to address that in a separate PR. - userSpaceOnUse gradients with transforms are not being applied correctly. This has been an issue in this repo forever, not a regression. Gotta figure out how to properly transform the gradient to the transformed path coordinate space. See https://github.com/dnfield/flutter_svg/pull/54/files#r216222991 and the test files added in that PR, which do not compile correctly today. * fix inheritence for gradient properties
This adds a new
package:animations
to the repository, which will contain fancy pre-built animations that can be customized with content and dropped into any application to delight its users.The first animation added in this patch is Material's Open Container Transition. It displays some kind of a container (e.g. a Card or Button) on screen and when tapped the container expands its size to take up the entire space of the surrounding navigator to reveal more content.
I am currently working on adding tests for this. In a follow-up PR I also plan to add an example application in which users can try out these effects.