Allow "from" hero state to survive hero animation in a push transition#32842
Allow "from" hero state to survive hero animation in a push transition#32842LongCatIsLooong merged 18 commits intoflutter:masterfrom
Conversation
HansMuller
left a comment
There was a problem hiding this comment.
This looks good. Just some trivial formatting feedback.
| setState(() { | ||
| _placeholderSize = box.size; | ||
| }); | ||
| setState(() => _placeholderSize = box.size); |
There was a problem hiding this comment.
We usually only use => for one-liners that return a value. That's why the original version of this statement was formatted with { }
Same comment below
There was a problem hiding this comment.
Done. But technically the expression evaluates to box.size, and setState does use the returned value (check it's runtime type). Not sure where to draw the line.
There was a problem hiding this comment.
It's true that setState() checks the return value, but only to warn the user if they've mistakenly returned a Future. I think one-liners that implement a void method should get curly braced.
| _proxyAnimation.parent = manifest.animation; | ||
|
|
||
| manifest.fromHero.startFlight(); | ||
| manifest.fromHero.startFlight(shouldIncludedChildInPlaceholder: manifest.type == HeroFlightDirection.push); |
There was a problem hiding this comment.
We need to be slightly careful here. If the user uses a shuttleBuilder, it could be reasonable for the user to expect that he/she can just use some globalkey that used to be in the from Hero inside the custom shuttleBuilder. That might break if we kept the original tree still under the Hero
There was a problem hiding this comment.
going to add a Limitations section in flightShuttleBuilder's documentation. Is this considered a breaking change?
There was a problem hiding this comment.
Then you'd have to explain that that's only true for the Hero of the from route in the event of a push navigation. Perhaps add another argument to Hero for preservePlaceholderStateDuringFlight that defaults to true and gets overridden when a custom placeholderBuilder is given?
There was a problem hiding this comment.
I think I did:
/// ## Limitations
///
/// Currently if a widget built by [flightShuttleBuilder] takes part in a
/// [Navigator] push transition, that widget or its descendants must not have
/// any [GlobalKey] that is used in the source Hero's descendant widgets.
Isn't flightShuttleBuilder == null always equal to preservePlaceholderStateDuringFlight?
In that case maybe we should add a Size parameter to placeholderBuilder?
| ? Visibility( | ||
| visible: false, | ||
| maintainState: true, | ||
| maintainAnimation: true, |
There was a problem hiding this comment.
ummm this is a bit unfortunate. @HansMuller it's not super clear to me why this is needed looking at the cascading logic in Visibility. Do you know?
There was a problem hiding this comment.
Not sure what you mean by "cascading logic". I agree that keeping the from Hero's animation running while it is hidden seems unnecessary.
There was a problem hiding this comment.
I just meant this series of logic
There was a problem hiding this comment.
Replacing the visibility widget with SizedBox(Offstage(TickerMode)) seems to work
| ? Visibility( | ||
| visible: false, | ||
| maintainState: true, | ||
| maintainAnimation: true, |
There was a problem hiding this comment.
I just meant this series of logic
| _proxyAnimation.parent = manifest.animation; | ||
|
|
||
| manifest.fromHero.startFlight(); | ||
| manifest.fromHero.startFlight(shouldIncludedChildInPlaceholder: manifest.type == HeroFlightDirection.push); |
There was a problem hiding this comment.
Then you'd have to explain that that's only true for the Hero of the from route in the event of a push navigation. Perhaps add another argument to Hero for preservePlaceholderStateDuringFlight that defaults to true and gets overridden when a custom placeholderBuilder is given?
|
Is there a link to the breaking change announcement? |
|
@Piinks there isn't one yet. I'll post the announcement once the changes are finalized. |
HansMuller
left a comment
There was a problem hiding this comment.
LGTM - just some suggestions about the docs
| /// [MaterialRectArcTween]. | ||
| typedef CreateRectTween = Tween<Rect> Function(Rect begin, Rect end); | ||
|
|
||
| /// A builder that builds a placeholder widget given a child and a [Size] |
There was a problem hiding this comment.
A function that builds a [Hero] placeholder widget ...
| /// A builder that builds a placeholder widget given a child and a [Size] | ||
| /// | ||
| /// The child can optionally be part of the returned widget tree. The returned | ||
| /// widget should typically be sized to [heroSize], if it doesn't do so |
| /// | ||
| /// If a widget built by [flightShuttleBuilder] takes part in a [Navigator] | ||
| /// push transition, that widget or its descendants must not have any | ||
| /// [GlobalKey] that is used in the source Hero's descendant widgets, as both |
There was a problem hiding this comment.
Better to break the "as both" point into a new sentence:
...widgets. Both subtrees will be included ... animation, and global keys must be unique across the entire widget tree.
| /// By default, an empty SizedBox keeping the Hero child's original size is | ||
| /// left in place once the Hero shuttle has taken flight. | ||
| final TransitionBuilder placeholderBuilder; | ||
| /// By default the placeholder widget left in place is an empty [SizedBox] |
There was a problem hiding this comment.
Please leave out "left in place"
...placeholder widget is an empty...
| final TransitionBuilder placeholderBuilder; | ||
| /// By default the placeholder widget left in place is an empty [SizedBox] | ||
| /// keeping the Hero child's original size, unless this Hero is a source Hero | ||
| /// of a [Navigator] push transition, in which case the placeholder will be a |
There was a problem hiding this comment.
This is run-on. Maybe:
...push transition. If this hero the source of a push transition, the [child] will be a descendant of the placeholder. The child will be kept [Offstage] during the hero's flight.
|
Hey @LongCatIsLooong , this is great. It looks like your branch needs to be updated to fix those codegen errors that are causing test failures. |
| /// [GlobalKey] that is used in the source Hero's descendant widgets. That is | ||
| /// because both subtrees will be included in the widget tree during the Hero | ||
| /// flight animation, and [GlobalKey]s must be unique across the entire widget | ||
| /// tree. |
There was a problem hiding this comment.
i don't understand this. Are you saying the child will be included twice in the tree? Because that won't work if the widget is stateful... the whole point of the Hero logic is that it only be included once.
|
cc @goderbauer |
* master: (24 commits) [flutter_tool,fuchsia] Update the install flow for packaging migration. (flutter#34447) SliverFillRemaining flag for different use cases (flutter#33627) SizedBox documentation (flutter#34424) Change API doc link to api.dart.dev (flutter#34388) 2589785 Roll src/third_party/skia 87e885038893..c3252a04b377 (3 commits) (flutter/engine#9327) (flutter#34484) ace5d59 Fix rawTypes errors in Android embedding classes (flutter/engine#9326) (flutter#34481) bf0def6 Roll src/third_party/skia 4c4945a25248..87e885038893 (1 commits) (flutter/engine#9325) (flutter#34471) Roll engine f1d821d..6f5347c (13 commits) (flutter#34466) Allow "from" hero state to survive hero animation in a push transition (flutter#32842) Roll pub dependencies (flutter#33677) Skip flaky test on Windows (flutter#34464) Allow flaky tests to pass or fail and mark web tests as flaky (flutter#34456) Dont depend on web SDK unless running tests on chrome (flutter#34457) Fix semantics_tester (flutter#34368) Include raw value in Diagnostics json for basic types (flutter#34417) Refactor Gradle plugin (flutter#34353) Allow web tests to fail in cirrus config (flutter#34436) skip bottom_sheet (flutter#34430) Remove unused flag `--target-platform` from `flutter run` (flutter#34369) Extract DiagnosticsNode serializer from WidgetInspector (flutter#34012) ...
| } | ||
|
|
||
| return SizedBox( | ||
| width: _placeholderSize?.width, |
There was a problem hiding this comment.
This breaks getting sematnics for a FloatingActionButton, e.g.
await tester.pumpWidget(MaterialApp(
home: FloatingActionButton(
onPressed: () {},
)));
expect(
tester.getSemantics(find.byType(FloatingActionButton)),
matchesSemantics(
hasTapAction: true,
hasEnabledState: true,
isButton: true,
isEnabled: true,
));
Now returns the semantics node for the parent of the widget rather than the semantics node for the widget.
Description
When a route push transition happens,
Herowidgets involved in the animation are all replaced by placeholder widgets (SizedBoxes). This removes their elements (as well as state) from the tree which might not be desirable for the fromHeroes if they are stateful.Related Issues
Fixes #31503 and #32356, somewhat mitigates #31473
Tests
I added the following tests:
Checklist
Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes (
[x]). This will ensure a smooth and quick review process.///).flutter analyze --flutter-repo) does not report any problems on my PR.Breaking Change
Does your PR require Flutter developers to manually update their apps to accommodate your change?