I have BottomAppBar with Row Icons.
BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
IconButton(),
IconButton(),
IconButton(),
],
))
I want to make an smooth animation so that when you click on an icon, it shrinks and then returns to its original size. I would call it a tap effect. I know a way to do it through a bunch of animationcontroller, but is there any way to make it easier, like AnimatedSize or else?
Something like this:
0: IconSize = 20.0
1: onPressed: IconSize = 15.0
2: IconSize = 20.0
Try to use the AnimatedContainer widget to do that and wrap it into a GestureDetector and change the width and the height of the container like this:
bool selected = false;
BottomAppBar(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
GestureDetector(child: AnimatedContainer(child: Icon(), width: selected ? 15.0 : 20.0), onTap: () {
setState(() {
selected = !selected;
});
},),
AnimatedContainer(child: Icon(), width: selected ? 15.0 : 20.0),
AnimatedContainer(child: Icon(), width: selected ? 15.0 : 20.0),
],
))
You can achieve that with an AnimatedBuilder, a TweenSequence and an AnimationController. The official documentation of the AnimatedBuilder explains the setup very well (even with a video).
Your TweenSequence could look like this:
final Animation<double> animation = TweenSequence(
<TweenSequenceItem<double>>[
TweenSequenceItem<double>(
tween: Tween<double>(begin: 20.0, end: 15.0)
.chain(CurveTween(curve: Curves.ease)),
weight: 50.0,
),
TweenSequenceItem<double>(
tween: Tween<double>(begin: 15.0, end: 20.0)
.chain(CurveTween(curve: Curves.ease)),
weight: 50.0,
),
],
).animate(myAnimationController);
Related
In CSS I can use filter to change brightness. It's easy to make button hover effect.
filter: brightness(1.1);
filter: brightness(0.9);
Is there any way to change widget brightness in Flutter?
If your main goal is giving a hover effect on a button, you can use the combination of animated container(for the effect) and inkwell(onHover method to notify when hovering happens)
AnimatedContainer(
height: 70,
duration: Duration(milliseconds: 200),
padding: EdgeInsets.only(
top: (isHover) ? 25 : 30.0, bottom: !(isHover) ? 25 : 30),
child: Container(
height: 30,
color: Colors.blue,
child: InkWell(
onTap: () {},
child: Text("Hover Button"),
onHover: (val) {
print("Val--->{}$val");
setState(() {
isHover = val;
});
},
),
We do this in flutter
Colors.red.withOpacity(1.1);
So I have a rotating container
Here is the working code:
#override
void initState() {
_controller = AnimationController(
vsync: this,
duration: Duration(seconds: 32),
)..repeat();
super.initState();
}
....
#override
Widget build(BuildContext context) {
double screenHeight = MediaQuery.of(context).size.height;
double screenWidth = MediaQuery.of(context).size.width;
return Container(
child: Column(
children: <Widget>[
SizedBox(
height: screenHeight,
child: Stack(
children: [
Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
],
),
Divider(),
Column(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Center(
child: AnimatedBuilder(
animation: _controller,
builder: (_, child) {
return Transform(
alignment: FractionalOffset.center,
transform: Matrix4.identity()
..setEntry(3, 2, 0.002)
..rotateY(0.5 * -math.pi)
..rotateZ(-0.1 * math.pi)
..rotateY((_controller.value - 0.6) * -math.pi)
..rotateX(0.5 * math.pi),
child: child,
);
},
child: Container(
//color: Colors.yellow.withOpacity(0.5),
height: 300,
width: 200,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(40),
boxShadow: [
BoxShadow(
color: Colors.yellow.withOpacity(0.5),
spreadRadius: 40,
//UNCOMMENTING THIS LINE CAUSES STRANGE BEHAVIOUR
//blurRadius: 50,
//offset: const Offset(0, 0),
),
],
),
),
//),
),
),
SizedBox(
height: 170,
)
],
),
],
),
),
],
),
);
}
However, when I uncomment the blurRadius line of code, it behaves strangely. Look how the container kind of gets distorted when it comes perpendicular to the screen.
I want it to be consistently blurred. This is something strange.
Shadows are very expensive for Flutter to draw, so it's bad to animate them like this because Flutter has to keep repainting the shadow. Since the shadow is also causing your transform problem, you should try to make the shadow you want in GIMP/Adobe or screenshot it in flutter, then add it to your project.
You should have an assets or images folder in your project directory, i.e, YOUR_FLUTTER_PROJECT/images. Put the shadow image in there.
Then, in the pubspec.yaml file, there should be a field for your assets. Add the path to the shadow file there, e.g.:
assets:
- images/a_dot_burr.jpeg
- images/a_dot_ham.jpeg
- images/YOUR_SHADOW_FILE
Run flutter pub get
In your code, replace your Container with this:
Image.asset('images/YOUR_SHADOW_FILE', height: 300, width: 200),
I haven't tested it, but I reckon it should still work.
I don't know the reasons behind it but removing the line ..setEntry(3, 2, 0.002) will remove the issues. However, there is a slight dimension issue during the animation which I thinks because of relativity. With the above line, it seems like animation decrease the blur effect with the increase of animation completion factor and reset to provided value when it reaches the starting point of animation.
I have created a container in which two AnimatedContainers are located. When i press the down button i would like the TextFieldContainer (The content with the white TextField in the middle) to disappear by decreasing it size to 0. Simountanesly i want to make the ToolbarContainer appear by increasing its size. So far this is what i get:
It looks like only one of the AnimatedContainers can animate its properties one at a time, which creates the werid behaviour as shown, where one of the AnimatedContainers animate quickly, then slowly and then quickly once again.
How do i make the animation smooth so the overall size of the main container does not change because the two AnimatedContainers animate their size equally quick and simountanesly?
Here is the code for the main container:
class _MessageToolbarContainerState extends State<MessageToolbarContainer> {
bool toggleToolbar = false;
#override
Widget build(BuildContext context) {
return Container(
width: widget.availableWidth - 75,
margin: EdgeInsets.only(bottom: 25.0),
constraints: BoxConstraints(minHeight: 50.0),
decoration: BoxDecoration(
color: Colors.blue,
borderRadius: BorderRadius.all(Radius.circular(25.0)),
border: Border.all(
color: Colors.grey,
width: 2.0,
),
),
child: Material(
borderRadius: BorderRadius.all(Radius.circular(25.0)),
elevation: 5.0,
clipBehavior: Clip.antiAlias,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
AnimatedContainer(
duration: Duration(seconds: 1),
constraints: BoxConstraints(
minHeight: toggleToolbar ? 0.0 : 50.0,
maxHeight: toggleToolbar ? 0.0 : 100.0,
),
child: IntrinsicHeight(
child: TextFieldContent(
onDownPressed: () {
setState(() {
toggleToolbar = !toggleToolbar;
print(toggleToolbar);
});
},
),
),
),
AnimatedContainer(
duration: Duration(seconds: 1),
constraints: BoxConstraints(
minHeight: !toggleToolbar ? 0.0 : 50.0,
maxHeight: !toggleToolbar ? 0.0 : 100.0,
),
child: IntrinsicHeight(
child: ToolbarContent(
onUpPressed: () {
setState(() {
toggleToolbar = !toggleToolbar;
print(toggleToolbar);
});
},
),
),
),
],
),
),
);
}
}
EDIT:
I have boiled the problem down to one widget. I have wraped my AnimatedContainers in two IntrinsicHeight widgets to keep their content from expanding to much. If i remove the IntrinsicHeight widgets i get the following output:
As it is clearly shown the AnimatedContainers now animate smoothly. Sadly that does not fix my problem. I added the IntrinsicHeight widgets to make the content expand as the user typed in the TextField. If i remove the IntrinsicHeight widgets the content of the AnimatedContainers are not displayed properly.
The original question still stands. How do I make the 2 AnimatedContainers animate properly when they are wrapped in an IntrinsicHeight widget?
Please check the following code. Regardless what I tried, the SizeTransition is not centered horizontally in the Column. I tried to wrap Column in a Container and then provide infinite width. I tried to wrap SizeTransition in a Center. I tried to wrap SizeTransition in a Container which has center alignment property. I tried to wrap it in a Stack. I tried to give the container child with alignment center property etc... But none of them works...
class MyWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: AnimatedBox(),
);
}
}
class AnimatedBox extends StatefulWidget {
#override
createState() => _AnimatedBoxState();
}
class _AnimatedBoxState extends State<AnimatedBox> with SingleTickerProviderStateMixin {
AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: Duration(milliseconds: 400),
);
}
#override
void dispose() {
_controller.dispose();
super.dispose();
}
#override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
RaisedButton(
child: Text('animate forward'),
onPressed: () {_controller.forward();},
),
RaisedButton(
child: Text('animate reverse'),
onPressed: () {_controller.reverse();},
),
const SizedBox(height: 100.0,),
SizeTransition(
child: Container(
width: 200.0,
height: 200.0,
color: Colors.blue,
),
sizeFactor: CurvedAnimation(
curve: Curves.fastOutSlowIn,
parent: _controller,
),
),
],
);
}
}
For instance, the following code does not work for SizeTransition, but works for ScaleTransition. I have no idea what's wrong with SizeTransition.
return Container(
width: double.infinity,
child: Column(
Despite the fact that my previous answer solves the problem to some extent, I also wanted to address how limited SizeTransition widget is and how to solve this.
SizeTransition provides the effect of "unfolding" its content, running the animation either in horizontal or in vertical axis by rewriting alignment settings.
To achieve the same effect without breaking alignment rules, but also avoid using ScaleTransition widget as we need the "unfold/reveal" animation and not "scale up" - here is what I propose:
#override
Widget build(BuildContext context) {
final _animation = CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn);
return Column(
children: <Widget>[
// ...,
AnimatedBuilder(
animation: _animation,
builder: (_, child) => ClipRect(
child: Align(
alignment: Alignment.center,
heightFactor: _animation.value,
widthFactor: null,
child: child,
),
),
child: Container(
width: 200.0,
height: 200.0,
color: Colors.blue,
child: Text("test"),
),
)
]
);
}
This is basically an AnimatedBuilder widget with the same ClipRect & Align used as in SizeTransition, except that it does limit alignment to one axis only.
If you'd like the animation to run in both horizontal & vertical axes - assign the same _animation.value to widthFactor property:
Align(
alignment: Alignment.center,
heightFactor: _animation.value,
widthFactor: _animation.value,
child: child,
),
This will help you achieve "reveal from center" effect without scaling up & down the content of your widget.
I can see that you tried many things already, here is some ideas I have:
#1
Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: // ...
)
or
#2
Column(
crossAxisAlignment: CrossAxisAlignment.stretch, // critical
children: <Widget>[
// ...,
Container(
alignment: Alignment.center, // critical
child: SizeTransition(
child: Container(
width: 200.0,
height: 200.0,
color: Colors.blue,
),
sizeFactor: CurvedAnimation(
curve: Curves.fastOutSlowIn,
parent: _controller,
),
),
),
]
)
Update
There is indeed a peculiar aspect of SizeTransition widget.
It has axis property that is set to Axis.vetical by default, which overrides the widget's horizontal alignment to -1.0 (start) and vertical alignment to 0.0 (center).
Changing that property to Axis.horizontal makes things work the other way around - aligning the widget horizontally to 0.0 (center) and vertically to -1.0 (start).
Solution:
SizeTransition(
axis: Axis.horizontal,
// ...,
)
Please let me know if this helped.
If you are using animated list view, you can use slide transition instead, just wrap your column around a slide transition widget and you should be good to go
SlideTransition(
position: Tween<Offset>(
begin: const Offset(0, -1), //The animated item will move upwards, use +1 to move downwards
end: Offset.zero,
).animate(
CurvedAnimation(parent: widget.animation, curve: Curves.fastOutSlowIn),
),
child: Column()
)
widget.animation is your Animation<double> variable (if inside stateful widget)
In the given code,onPressed on the raised button works and translate FlatButton to the top. But onPressed on FlatButton is not working
#override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Transform(
transform: Matrix4.translationValues(
0.0,
_translateButton.value,
0.0,
),
child: FlatButton(
onPressed: () {
print('tapped Flat button');
},
child: Text('upper'),
),
),
RaisedButton(
onPressed: () {
animate();
print('tapped Raised button');
},
child: Text('lower'))
],
);
}
Here _translatebutton value changes from 0 to -60 when animate() is called
_animationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
_translateButton = Tween<double>(
begin: 0,
end: -60,
).animate(CurvedAnimation(
parent: _animationController,
curve: Interval(
0.0,
0.75,
curve: _curve,
),
));
Wrap the Transform widget in a SizedBox (expanded or from size, depending on your requirement.
I came across this problem last week and in my case, the composition was like this:
Stack(
children: [
Widget0,
Widget1,
Opacity(
opacity: sth,
child: Transform.translate(
offset: Offset(sth),
child: Transform.rotate(
angle:sth,
child: FlatButton(
onPressed: (){sth},
child: Text("sth"),
),
),
),
),
],
)
Based on the suggestion of rahulthakur319 on the issue number
27587
I wrapped my Transform.translate composition inside a new Stack and I wrapped the stack inside a Container. Remember that the new Container should have enough width and height to show its child. I personally used MediaQuery.of(context).size.
it's working even during the complex series of animations.
The final code:
Stack(
children: [
Widget0,
Widget1,
Opacity(
opacity: sth,
child: Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: Stack(
children: [
Transform.translate(
offset: Offset(sth),
child: Transform.rotate(
angle: sth,
child: FlatButton(
onPressed: (){sth},
child: Text("sth"),
),
),
),
],
),
),
),
],
)
If your translation moves the button outside the area of the stack, the button no longer reacts to clicks. A simple way to test this is to wrap your Stack widget in a container and color it blue (or anything obvious), and if your button is moved outside the blue area, you know it's losing its clickability because it's outside of the Stack.
If this indeed is the issue, the solution is to keep the Stack inside the container, and then either set the container dimensions such that the button still stays within the border after translation, or reposition widgets relative to the container such that the translation stays within the border.
If someone is still trying to solve this issue, I solved it by wrapping the widget with IgnorePointer widget on which I don't want the pointer to reach.
reference from here
The answer I got was that in a View if an element is translated then the animation works correct but the click property is altered in someway that we can't use it after translating the element
I had this same issue my Switch widget was not working in the Stack.
The solution i found was to include it in SizeBox or Container with fixed width and height.
if your switch widget is in Row try to add Constraints on Row with SizeBox rather than adding it in every widget.