SlideTransition in flutter takes space before it slides in - flutter

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

Related

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?

flutter scollable.ensurevisible not working in reverse direction

Please check the video / gif:
I have a pageview that will make the current tab active. I need to ensure the active tab is always visible even if the user swipes the screen multiple times. It is working from the left to right. But when we try back from right to left it's not behaving as expected.
PageView file
PageView(
controller: pageController,
onPageChanged: (int page) {
_duaWidgetState.currentState.updateBtn(page + 1);
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext);
},
children: loaded
TabBarWidget with scroll view file
GlobalKey activeBtn = GlobalKey();
var _selectedTab = 1;
#override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.symmetric(vertical: 20),
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: List.generate(widget.numberOfTab,
(index) => tabBarItem(index + 1, widget.numberOfTab, activeBtn)),
),
),
);
}
Container that uses the Key
Container(
key: activeBtn,
margin: EdgeInsets.only(left: 20),
padding: EdgeInsets.symmetric(
vertical: 10,
horizontal: 24,
),
No One answered my Question. But, after a lot of effort and time. I found a workaround to fix this problem. Hope this is a bug and will fix by the flutter team in the coming updates.
Scrollable.ensureVisible(_duaWidgetState.currentState.activeBtn.currentContext);
This function has an optional argument called alignment which can be adjusted to make sure the selected element/button is in the center of the viewport. (In my case horizontally center).
My widgets are horizontally scrollable, so I need to update the page number according to the result from PageView(), Here when I used
alignment: 0
Its working fine from left to right swipes of page view. But with this alignment when i swipe page from right to left to go to previous page in the PageView widget, the ensureVisible is not working like expected. The selected element/button is out of the view. I experimented and found that when I used
aligment: 1
The ensureVisible is working fine from the swipes from right to left but at the same time. when I scroll left to right the same problem occured.
Finally, so I managed to do some workaround to fix this behavior. The solution is to store last page index in a variable and check whether the last page is greater than the new page then alignment: 0 and if the last page less than the new page aligment:1 like that.
PageView(
onPageChanged: (int page) {
if (lastPage < page) {
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext,
alignment: -0.0100,
);
} else {
Scrollable.ensureVisible(
_duaWidgetState.currentState.activeBtn.currentContext,
(Perfect For font size 24)
alignment: 1.005,
);
}}
Hope my answer will help someone in the future.
I modified the original ensureVisible() a bit and this works in both directions in my case:
// `contentAnchor` is the global key of the widget you want to scroll to
final context = contentAnchor.currentContext!;
final ScrollableState scrollable = Scrollable.of(context)!;
scrollable.position.ensureVisible(
context.findRenderObject()!,
duration: const Duration(milliseconds: 300),
curve: Curves.easeInOut,
);

Make first item (or padding) of SingleChildScrollView not scrollable and delegate the touch events

I have a Stack with two Columns.
First column contains contains only one child (1) with InteractiveViewer which has defined height (for example 250). It changes its content's zoom and translation based on scroll offset of SingleChildScrollView from second column. It shouldn't be pushed out of the screen while scrolling, so it's under the scroll view in a stack.
The second column have SingleChildScrollView with top padding (2) OR first view (3) that matches the (1) height.
Now I'd like to make the top padding (2) or view (3) not scroll the SingleChildScrollView but pass those touch events to InteractiveViewer. Doesn't matter whether the solution use padding or empty view, I just wanted to note here than what I want can be achieved with padding or view. I tried both but failed.
I tried the GestureDetector, IgnorePointer and AbsorbPointer, but seems like the SingleChildScrollView always get the touch events. I haven't found a way to make the top padding not scrollable too.
Basically I'd like to achieve something similar to the attached gif, except that I don't need the "Collapsing Header" text animation and app bar. Just pay attention to the mountains that hide below the scroll view. The scroll view should take entire screen once the scroll offset is equal padding/view height (250 in this example).
Is that possible somehow? My logic behind InteractiveViewer is way more complicated than the example provided below, I just simplified it to make the question easier to understand.
Stack(children: [
Column(
children: [
Container( // (1) Widget that should get the touch events
height: 250,
child: InteractiveViewer(...)
),
],
),
Column(
children: [
Expanded(
child: SingleChildScrollView(
padding: EdgeInsets.only(top: 250), // (2) Either not scrollable padding
child: Column(
children: [
Container(height: 250), // (3) or not scrollable first item
Container(...)
],
),
),
),
],
),
]);

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

Is using AnimatedOpacity to hide and show complex widgets not effective in 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.