Repository navigation
Animate changes to IndexedStack #48217
Description
Hi.
Use case
This is about two very useful widgets:
Intuitively, it seems they could be combined in order to preserve the state between two widgets while animating the transition from one to the other. However, the following code will not work as expected:
AnimatedSwitcher(
transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder,
duration: const Duration(milliseconds: 500),
child: IndexedStack(
index: condition ? 1 : 0,
children: [
WidgetOne(),
WidgetTwo(),
],
),
)This does not work for obvious reason (assuming AnimatedSwitcher is rebuilt with a new condition value): the IndexedStack widget itself did not change, so there is no animation (while widgets are correctly replaced).
Proposal
I would like to suggest the addition of a new parameter or some kind of workaround to facilitate the use of AnimatedSwitcher with IndexedStack. Unfortunately, I don't know what this workaround should look like.
This is related to this issue: #39398. It was closed for the reason that it is "related to organising of widgets". I would like to re-open it as a "feature request".
A Stackoverflow question has been open consequently. As a Flutter beginner, I'm not comfortable with the proposed answers as they require to completely re-implement IndexedStack and / or Animation which I find prone-to-error compared to simply combine AnimatedSwitcher with IndexedStack (that seemed so much more convenient!).
Is there a possibility to make these two widgets more "user-friendly" when they are used together?
Activity
[-]Ease the combined use of "AnimatedSwitcher" with "IndexedStack"[/-][+]Animate changes to IndexedStack[/+]hoshinokanade commentedon Jan 6, 2020
I understand you want the Flutter framework to provide your desired functionality out of the box.
I have read your stackoverflow question, and I think using both
AnimatedSwitcherandIndexedStacktogether should be able working properly for you after digging into the documentation.Flutter prefer widgets for a single use-case. By combining different widgets you get a board spectrum of functionality. Welcome to give further suggestion for feature request if you still not comfortable because many developers may want this feature.
Delgan commentedon Jan 6, 2020
Hi @hoshinokanade, thanks for your answer.
Correct me if I'm wrong, but I tested your workaround and it seems it doesn't work well with
IndexedStack:WidgetOne()andWidgetTwo()states are not preserved, they are rebuilt each time a transition occurs. 😕jeroen-meijer commentedon Jan 8, 2020
Can confirm, the state is not preserved.
I believe this reason is because you change the
IndexedStackskeyevery time a page change occurs. The framework will then (correctly) throw away and rebuild theIndexedStack, and as a result the state is not preserved.sondresorbye commentedon Apr 12, 2020
I want to use IndexedStack with the FadeThrough animtion from the animation library: https://pub.dev/packages/animations, but I am not able to do this becuase of this
diegoveloper commentedon Jul 10, 2020
Check my answer here https://stackoverflow.com/a/59535289/666221
nt4f04uNd commentedon Dec 1, 2020
@sondresorbye0111 checkout this #56662 (comment) works well for me
JasCodes commentedon Dec 28, 2020
How about using my package? https://pub.dev/packages/animated_indexed_stack
petro-i commentedon Jan 29, 2021
None of the solutions provided corresponds to initial requirements!
Please take into account that:
AnimatedSwitcher smoothly animates between child's (animate-out-old-animate-in-new), IndexedStack maintains child's state. Exact this combination of properties is required.
Still waiting for solution.
p4-k4 commentedon Nov 22, 2021
TEGRAXD commentedon Jan 9, 2022
globalwebforce commentedon Jun 24, 2022
globalwebforce commentedon Jun 24, 2022
Here's the best workaround for now, it has 80,000 score in StackOverflow
https://stackoverflow.com/a/59535289/10494346
maheshj01 commentedon Jul 19, 2022
@twoco You can try using FadeTransition, You can read about the implementation in this blog post on point 4.
demo: https://dartpad.dev/?id=894922ccb67f5fdc4ffb652e41916fa2
akshdeep-singh commentedon Oct 15, 2022
I am using this method:
Though it is a workaround, but is very simple
and it also has exit animation for the index we are switching from.
code sample
clragon commentedon Jul 16, 2023
While we wait for a change in the framework, I have written my own solution based on
IndexedStackandPageTransitionSwitcher.It uses internal
ChildEntrysto keep track of children, just likePageTransitionSwitcherand it usesVisibilityto offstage invisible widgets just likeIndexedStack.The code depends on the
animationspackage for its default values. However, it would be possible to copy those defaults and type declarations into the file itself to make it independant.To make sure all animations are in correct order and our state is kept alive, we internally use
GlobalKeysassigned to eachChildEntry. Because of that, all ourchildrenshould have a uniquekey. It does not have to be global, just a regularValueKeywill do for identification.The code can be found here:
https://gist.github.com/clragon/f26fe2fc2cc1574243c3407277e2997e
(Or in this section, in case I ever delete that gist:)
Source Code
I also have a piece of demonstration code:
Demo Code
Which we can see in this video:
ZN4El9wVeM.mp4
Visible in the video are the most important points; our Timer in the pages does not reset when switching between tabs, as the State is kept alive. The transitions work correctly, with reverse specified. The children are removed once they have animated out.
The debug overlay shown in the video is not in the gist, as its not useful for production.
But I will include it here, in case somebody wants to test around with it. This is simply a replacement for the build method of the
AnimatedIndexedStack:Debug Code
Additionally, I have experimented with getting the same transitions as a
MaterialPageRoute.I thought I might be able to simply access
Theme.of(context).pageTransitionsThemeand use it, however, it demands aRouteto build the transitions. It seems as of right now, there is no way of retrieving these animations without passing aRoute. This is unfortunate, but we can hack around this requirement by simply passing the surrounding ModalRoute.However, This leads to weird artefacting from
ZoomPageTransitionbecause it uses so called snapshots. We can disable these with a workaround and eventually arrive at a hacky but working animated indexed stack that functions similar to a navigator without being one.To disable the snapshots, we have to override the page transition theme. We can do that locally just for our AnimatedIndexedStack usage, like so:
Transition Code
which we can then use like this:
I hope this is useful to someone out there, for the time until we have a proper inbuilt solution.
maheshj01 commentedon Dec 8, 2023
Here's a custom implementation of FadeTransition with IndexedStack built on top of the official sample
Unfortunately with Indexed Stack it doesn't seem to fade well.
Indexed stack
Screen.Recording.2023-12-08.at.16.32.15.mov
Stack
Screen.Recording.2023-12-08.at.16.28.42.mov
complete code sample
shubhamsinghshubham777 commentedon Sep 18, 2024
This simple code has worked for my simple requirements so far. Hope it helps somebody until the Flutter team comes up with an actual solution/implementation.
Btw I've created an easy to use library (a legit drop-in replacement 😂) using this idea.
Demo
Screen.Recording.2024-09-19.at.2.28.34.AM.mp4
Simple Usage
Code
vasilich6107 commentedon Dec 10, 2024
For anyone who will find this issue - tried all examples mentioned above - unfortunately none of them was able to achieve the crossfade behavior
krll-kov commentedon Jan 24, 2025
Working solution that maintains state, uses cross fade effect and lazy builds children
intraector commentedon Jul 20, 2025
Previous solutions did not work well for me. For example, if there are GlobalKeys in the children (I use multiple Navigators) you going to have error "Multiple widgets used the same GlobalKey".
So here is my solution, which takes a screenshots and fades it away while fading in the active child: