Skip to content
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

Merged
merged 19 commits into from
Nov 26, 2019
Merged

Adds animations package with open container transition #30

merged 19 commits into from
Nov 26, 2019

Conversation

goderbauer
Copy link
Member

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.

@goderbauer goderbauer requested a review from HansMuller August 28, 2019 20:49
Copy link
Contributor

@HansMuller HansMuller left a 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
Copy link
Contributor

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(
Copy link
Contributor

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.
Copy link
Contributor

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
Copy link
Contributor

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,
Copy link
Contributor

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) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert value != null here?

Copy link
Member Author

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;
Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe isVisible because bool?

Copy link
Contributor

@HansMuller HansMuller left a 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);
Copy link
Contributor

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

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

Copy link
Member Author

@goderbauer goderbauer left a 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);
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

Copy link
Contributor

@HansMuller HansMuller left a 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;
Copy link
Contributor

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.

Copy link
Member Author

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
Copy link
Contributor

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?

Copy link
Member Author

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.
Copy link
Contributor

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?

Copy link
Member Author

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(
Copy link
Contributor

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

Copy link
Member Author

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(
Copy link
Contributor

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()

Copy link
Member Author

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,
Copy link
Contributor

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.

Copy link
Member Author

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
Copy link
Contributor

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;
Copy link
Contributor

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),
  ),
);

Copy link
Member Author

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.

@goderbauer goderbauer merged commit 0b809df into flutter:master Nov 26, 2019
@goderbauer goderbauer deleted the animations branch November 26, 2019 17:54
stuartmorgan pushed a commit that referenced this pull request Oct 31, 2024
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants