Is using AnimatedOpacity to hide and show complex widgets not effective in Flutter? - flutter

I'm using AnimatedOpacity to hide some widgets in Stack between various states in my app.
For instance I have Stack with ListView and animating loading indicator (shimmer) below, so that when list is populated the animated background opacity is gradually set to 0.
Stack:
- AnimatedOpacity (
opacity: _populated ? 0.0 : 1.0,
child: AnimatedLoadingWidget,
),
- ListView
Do I understand correctly that the animation in AnimatedLoadingWidget continues and is rendered even if opacity is set to 0.0? Does it have a performance impact on the app?

If you're wondering whether or not the widget is still rendered when an opacity of 0, then no.
Opacity and its animated variant is clever enough to not render the child if the opacity is strictly equal to 0.
But that's not enough. Even with an opacity of 0, your loading widget is still in the widget tree, and its animation continues to play.
To be more specific, with typical:
Opacity(
opacity: 0,
child: const CircularProgressIndicator(),
)
the spinner will not be visible, but it will still endlessly request new frames.
To fix that, we can use TickerMode widget like so:
Opacity(
opacity: 0,
child: TickerMode(
enabled: false,
child: const CircularProgressIndicator(),
),
)
Doing so will "mute" animations played using the animation framework, and as such, the spinner will stop requesting for new frames.
Alternatively, you can use AnimatedCrossFade with a custom layoutBuilder to achieve a similar effect.

Related

When using Flutter Visibility widget, can the change in visibility be animated?

I have a column holding a number of containers. The top and bottom containers are always shown, but the ones between sit inside a Visibility widget. If the user taps the screen, the middle containers are made visible, and then are made invisible again on the next tap. So essentially, the column expands and contracts each time it's tapped.
I'd like the expansion and contraction to happen a bit more gracefully - currently it looks very harsh when the middle containers just suddenly appear. Is there any way of animating the way in which the containers are made visible and hidden? Or should I be looking at using a totally different Widget than Visibility?
You can use an animated crossfade. Make the first child as SizedBox.shrink() and second child as the widget that you wish to display and it should be animating as expected. And switch according to the requirement
bool _first = true;
AnimatedCrossFade(
duration: const Duration(seconds: 1),
firstChild: const SizedBox.shrink(),
secondChild: const FlutterLogo(style: FlutterLogoStyle.stacked, size: 100.0),
crossFadeState: _first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
)
Alter the value of _first to true or false.

IntrinsicHeight makes size animation broken

I have a layout where I have a row and inside this row, I have a custom expansion tiles list and a custom widget, which is a rectangle with a fixed width of 5 pixels, the height of the rectangle must be the same, as the expansion tile's height.
To make the rectangle the same height as the expansion panel, I used intrinsic height.
So, the structure is this IntrinsicHeight: Row: [ExpansionPanel, Container]
The problem is that when I use IntrinsicHeight all animations are broken.
Animation in the expansion tile list is working, but the container which contains the row does not resize following the animation, but does it without any intermediate steps.
I have tried to use different types of animation.
Implicit tween animation builder
return TweenAnimationBuilder(
tween: Tween<double>(begin: 0, end: opened ? 1 : 0),
duration: Duration(milliseconds: 600),
builder: (_,double value, __){
return ClipRect(
clipBehavior: Clip.antiAlias,
child: Align(
alignment: Alignment.bottomCenter,
heightFactor: value,
child: value != 0 ? body : null,
),);
}
);
Cross fade animation
return AnimatedCrossFade(
firstChild: body,
secondChild: Container(height: 0,),
duration: Duration(milliseconds: 600),
crossFadeState: opened ? CrossFadeState.showFirst : CrossFadeState.showSecond,
sizeCurve: Curves.ease,
);
Changing animation type didn't fix my problem.
My questions are following:
Is there an option to make an animation of size simultaneously using IntrinsicHeightWidget
If there is not such an option, Is there an option to make a Rectangle widget with the same height as an expansion tile list, but without the IntrinsicHeight widget.
I have captured a video to show how the problem looks like.
https://www.youtube.com/watch?v=xpQ8QHwHUWQ
I have seen similar problems, but I didn't get success implementing them in my case.
Flutter - Animate change on height when child of container renders
Stretch children of a Flutter Row to the maximum *natural* height
How to stretch Containers inside Row to maximum available height?

How to make hero widget fly below other widget in route transition

How to make hero widget fly below appBar and bottomBar widgets in route transition.
I had tried to warp other widgets with hero also but no luck.
During a transition, Flutter places the Hero's in the order they are found in the widget (element) tree; last one on top. So you might be able to organise your screen in such a way that, the app bar and bottom bar heros are found later in the tree than the body containing other heros. For example with a Stack:
Stack(
children: [
Body(),
Positioned(
top: 0,
left: 0,
right: 0,
child: AppBar(),
),
Positioned(
bottom: 0,
left: 0,
right: 0,
child: BottomBar(),
),
],
)
This relies on the implementation details of Flutter's heros, so it is rather hacky.
Another approach would be to copy Flutter's HeroController, Hero and related code and modify it to order the Hero OverlayEntries by a new "z-index" property of the Hero widget.
The Heros "fly" in the Navigator's Overlay. So yet another approach could be to place your app bar and bottom bar on top of the Navigator (for example with a Stack like above).
None of these are great of course.

Fade in/out FloatingActionButton?

Is there any way to slowly fade-in and out a floating action button? I have in my list a FAB to offer the user a way to quickly scroll to the top of the list.
So far I've found in various posts how to detect when the list is scrolled to the top or away from the top (https://medium.com/#diegoveloper/flutter-lets-know-the-scrollcontroller-and-scrollnotification-652b2685a4ac), but I fail to find how I can change the alpha value of a widget. Any idea how to solve this?
Thanks
Martin
This example is from flutter dev page:
With a AnimatedOpacity you can fade any widget.
Just put you floating action button as child of AnimatedOpacity.
Here is some example:
AnimatedOpacity(
// If the widget is visible, animate to 0.0 (invisible).
// If the widget is hidden, animate to 1.0 (fully visible).
opacity: _visible ? 1.0 : 0.0,
duration: Duration(milliseconds: 500),
// The green box must be a child of the AnimatedOpacity widget.
child: Container(
width: 200.0,
height: 200.0,
color: Colors.green,
),
);
You can find more info and full code here: https://flutter.dev/docs/cookbook/animation/opacity-animation

SlideTransition in flutter takes space before it slides in

I implemented SlideTransition for a widget in flutter, and it slides in as expected. The issue is that before the animation is called, the space where the slider is going to be displayed later is empty.
How can I make the parent to give this space to other widgets in the layout until the moment that a slider comes into view?
I was thinking about giving a slider the initial height of zero, but then the widgets inside the slider would act funny as the height changes in sync with sliding. I wonder if there is a simpler way.
The parent is:
new Scaffold(
appBar: _appBar,
body: new Column(
children: <Widget>[
new Expanded(
child: _body;
}),
),
new SlideTransition(
position: _sliderPosition, //
child: _slider,
),
],
);
And the position is defined as:
_sliderPosition = new MaterialPointArcTween(
begin: const Offset(0.0, 1.0),
end: Offset.zero,
).animate(_animationController);
I solved this issue by replacing _slider with _buildSlider() which returns null until a slider is needed.
This is also better for performance as there is no need to render a slider if it's hidden.
Bumped into the same issue with a SlideTransition taking the full height during a vertical slide. In the end I achieved the slide effect by using a SizeTransition instead, and setting its axisAlignment property accordingly (-1, 0, 1 for top/center/bottom in my case).