I've run into a really weird issue. This is like some type confusion?
If I write my animation like this, it works as expected:
Widget _animatedButtonsBuilder(BuildContext context, LoginState state) {
final animTarget = state.isPhoneNumberFocused
? _controller.lowerBound
: _controller.upperBound;
_controller.animateTo(animTarget);
final double width = MediaQuery.of(context).size.width;
//final transform = Matrix4.translationValues(_animation.value * width, 0.0, 0.0)..scale(1.0, 1.0-_animation.value, 1.0);
return AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return Transform(
transform: Matrix4.translationValues(_animation.value * width, 0.0, 0.0)..scale(1.0, 1.0-_animation.value, 1.0),
child: new RaisedButton(
child: const Text('Login'),
color: Color(0xff938065),
elevation: 4.0,
splashColor: Colors.blueGrey,
textColor: Color(0xffffffff),
onPressed: () {},
));
});
}
but if I uncomment
//final transform = Matrix4.transla...
and passed that to the transform: parameter, it doesn't seem to work right.
What's happening?
I think to get equivalent result you'd need to move the line into the builder,
because there it is executed every time the animation progresses, not only at the start of the animation.
Widget _animatedButtonsBuilder(BuildContext context, LoginState state) {
final animTarget = state.isPhoneNumberFocused
? _controller.lowerBound
: _controller.upperBound;
_controller.animateTo(animTarget);
final double width = MediaQuery.of(context).size.width;
return AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
//final transform = Matrix4.translationValues(_animation.value * width, 0.0, 0.0)..scale(1.0, 1.0-_animation.value, 1.0);
return Transform(
transform: Matrix4.translationValues(_animation.value * width, 0.0, 0.0)..scale(1.0, 1.0-_animation.value, 1.0),
child: new RaisedButton(
child: const Text('Login'),
color: Color(0xff938065),
elevation: 4.0,
splashColor: Colors.blueGrey,
textColor: Color(0xffffffff),
onPressed: () {},
));
});
}
Related
I have created my custom stateful widget and I am taking a boolean that I use to check the state of the AnimatedContainer. Also, there's a function in createState to check the state of the AnimatedContainer and change the width of the container. My problem is that I am trying to use the function _handleTap() in AnimatedContainer as I child but it gives me an error saying that the expression has a type of void therefore, can't be used.
class SectionTaps extends StatefulWidget {
SectionTaps(this.isActive);
bool isActive = false;
_SectionTapsState createState() => _SectionTapsState();
}
class _SectionTapsState extends State<SectionTaps> {
bool _isActive = false;
double _width = 255.0;
void _handleTap(){
setState(() {
_isActive = widget.isActive;
_isActive == true ? _width = 55.0 : _width = 255.0;
//change container width
});
}
final leftButton = new AnimatedContainer(
duration: Duration(seconds: 1),
height: 88.0,
width: 255.0,
decoration: new BoxDecoration(
color: new Color(0xFF376480),
shape: BoxShape.rectangle,
borderRadius: new BorderRadius.only(
topRight: Radius.circular(80.0),
bottomRight: Radius.circular(80.0),
),
boxShadow: <BoxShadow>[
new BoxShadow(
color: Colors.black12,
blurRadius: 10.0,
offset: new Offset(0.0, .0),
),
],
),
child: _handleTap(),
);
You need to Wrap your - AnimatedContainer with GestureDetector & use onTap: to call setSate().
Code:
class SectionTaps extends StatefulWidget {
SectionTaps(this.isActive);
bool isActive = false;
_SectionTapsState createState() => _SectionTapsState();
}
class _SectionTapsState extends State<SectionTaps> {
bool _isActive = false;
double _width = 255.0;
void _handleTap() {
setState(() {
print ('Set State Called.');
_isActive = widget.isActive;
_isActive == true ? _width = 55.0 : _width = 255.0;
//change container width
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Center(
child: GestureDetector(
onTap: () {
_handleTap();
},
child: new AnimatedContainer(
duration: Duration(seconds: 1),
height: 88.0,
width: 255.0,
decoration: new BoxDecoration(
color: new Color(0xFF376480),
shape: BoxShape.rectangle,
borderRadius: new BorderRadius.only(
topRight: Radius.circular(80.0),
bottomRight: Radius.circular(80.0),
),
boxShadow: <BoxShadow>[
new BoxShadow(
color: Colors.black12,
blurRadius: 10.0,
offset: new Offset(0.0, .0),
),
],
),
),
),
),
);
}
}
Code:
void showNow() {
showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation, Animation<double> secondaryAnimation) {
return Container(
width: 200.0,
height: 200.0,
color: Colors.orange,
);
},
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black,
transitionDuration: const Duration(milliseconds: 200),
);
}
But this dialog takes up the entire space instead of 200.0 width and height that I used. I tried wrapping Container in Scaffold, but then barrierDismissible and barrierColor doesn't seem to work. I changed their values too but no luck.
Wrap your Container in Wrap Widget :
void showNow() {
showGeneralDialog(
context: context,
pageBuilder: (BuildContext buildContext, Animation<double> animation,
Animation<double> secondaryAnimation) {
return Wrap(
children: <Widget>[
Container(
width: 200.0,
height: 200.0,
color: Colors.orange,
),
],
);
},
barrierDismissible: true,
barrierLabel: MaterialLocalizations.of(context).modalBarrierDismissLabel,
barrierColor: Colors.black,
transitionDuration: const Duration(milliseconds: 200),
);
}
I'm doing a backdrop style slide down similar to the Codelabs flutter example here. I'd like the foreground part to stop at the bottom of the backdrop content.
I know you can't get the height directly until after layout. Since the app starts with the foreground part at the top, I was thinking I could get the height after build and store it in the state, but I'm not exactly sure how to do this.
This is what I have now, just using a predefined 'overhang' that doesn't account for the backdrop content height:
Widget _buildForeground(BuildContext context, BoxConstraints constraints) {
Animation<RelativeRect> rectAnimation = new RelativeRectTween(
begin: new RelativeRect.fromLTRB(0.0, constraints.maxHeight - widget.overhang, 0.0, 0.0),
end: new RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller);
return new PositionedTransition(
rect: rectAnimation,
child: new Material(
shape: RoundedRectangleBorder(borderRadius: new BorderRadius.only(topLeft: new Radius.circular(15.0), topRight: new Radius.circular(15.0))),
elevation: 16.0,
child: widget.foreground,
)
);
}
#override
Widget build(BuildContext context) {
return new LayoutBuilder(
builder: (context, constraints) => new Stack(
children: <Widget>[
new Container(color: Theme.of(context).primaryColor,),
widget.background,
_buildForeground(context, constraints),
],
),
);
}
Thanks to Remi I came up with this solution using GlobalKey
void _toggleForeground() {
setState(() => top = backgroundKey?.currentContext?.size?.height);
_controller.fling(velocity: _isBackgroundVisible ? -widget.toggleVelocity : widget.toggleVelocity);
}
Widget _buildForeground() {
if (top == null) top = 500.0;
Animation<RelativeRect> rectAnimation = new RelativeRectTween(
begin: new RelativeRect.fromLTRB(0.0, top, 0.0, 0.0),
end: new RelativeRect.fromLTRB(0.0, 0.0, 0.0, 0.0),
).animate(_controller);
return new PositionedTransition(
rect: rectAnimation,
child: new Material(
shape: RoundedRectangleBorder(borderRadius: new BorderRadius.only(topLeft: new Radius.circular(15.0), topRight: new Radius.circular(15.0))),
elevation: 16.0,
child: widget.foreground,
)
);
}
#override
Widget build(BuildContext context) {
return new Stack(
children: <Widget>[
new Container(color: Theme.of(context).primaryColor),
new Column(
key: backgroundKey,
mainAxisSize: MainAxisSize.min,
children: <Widget>[widget.background],
),
_buildForeground(),
],
);
}
I'd like to animate a gap between two items in a list. I thought of using an AminatedContainer with a height initially at zero but I'm not familiar with how to make this work. My code at the moment is:
new AnimatedContainer(
duration: const Duration(milliseconds: 200),
height: App.itemSelected==id ? 50.0 : 0.0,
curve: Curves.fastOutSlowIn,
),
That does change the height of the Container but not in an animated way as I had hoped. Any help would be gratefully received!
I am not sure if AnimatedSize is suitable for your use case, but I have added an example on how to make a simple animation with it:
The coloring is a bit off due to the recording but you should be able to test this yourself.
class MyAppState extends State<MyApp> with TickerProviderStateMixin {
double _height = 50.0;
double _width = 20.0;
var _color = Colors.blue;
#override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new AnimatedSize(
curve: Curves.fastOutSlowIn, child: new Container(
width: _width,
height: _height,
color: _color,
), vsync: this, duration: new Duration(seconds: 2),),
new Divider(height: 35.0,),
new Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new IconButton(
icon: new Icon(Icons.arrow_upward, color: Colors.green,),
onPressed: () =>
setState(() {
_color = Colors.green;
_height = 95.0;
})),
new IconButton(
icon: new Icon(Icons.arrow_forward, color: Colors.red,),
onPressed: () =>
setState(() {
_color = Colors.red;
_width = 45.0;
})),
],
)
],)
,)
);
}
}
You can use an AnimatedSize for that purpose.
https://api.flutter.dev/flutter/widgets/AnimatedSize-class.html
I'm looking to animate the scale of a container using the transform property of an AnimatedContainer; however, the scale is not being transitioned and jumps directly from start to end.
Code Snippet:
var container = new AnimatedContainer(
duration: const Duration(milliseconds: 200),
width: 50.0,
height: 50.0,
// selected is a bool that will be toggled
transform: selected ? new Matrix4.identity() : new Matrix4.identity().scaled(0.2,0.2),
decoration: new BoxDecoration(
shape: BoxShape.circle,
backgroundColor: Colors.blue[500],
),
child: new Center(
child: new Icon(
Icons.check,
color: Colors.white,
),
)
);
Any insight on what's going on?
AnimatedContainer supports animting it's transform value, as follow:
/// scale to 95%, centerred
final width = 200.0;
final height = 300.0;
bool shouldScaleDown = true;// change value when needed
AnimatedContainer(
color: Colors.blueAccent,
width: width,
height: height,
duration: Duration(milliseconds: 100),
transform: (shouldScaleDown
? (Matrix4.identity()
..translate(0.025 * width, 0.025 * height)// translate towards right and down
..scale(0.95, 0.95))// scale with to 95% anchorred at topleft of the AnimatedContainer
: Matrix4.identity()),
child: Container(),
);
I'm afraid transform is one of the properties we don't animate (child is another). If you want to animate the scale, you can use ScaleTransition.
ScaleTransition: https://docs.flutter.io/flutter/widgets/ScaleTransition-class.html
Bug for Matrix lerp: https://github.com/flutter/flutter/issues/443
you can Animate using the Animated Builder,the below code will scale a Text from font Size 20-35 in 4 secs let me break this into steps to make you better understand
1.you need to implement your class from TickerProviderStateMixin.
2.You need an AnimationController and a Animation variables;
3.wrap your widget inside an AnimatedBuilder (note AnimatedBuilder must return a Widget at least a container) and add a controller to the animation as
animation: _controller,
and builder Which returns the AnimatedWidget
4.In the init method intialize the controller with vsync and the Duration of Animation.
and the animation with the Tweenit takes begin and end values which define the initial and final values of your Widget to be animated
for me, in this case, it was text widget so the begin and end Values will be corresponding to the fontSize.which receives variable values as animation.value
5.Finally How do you want the animation effect To be will be specified by the animate which takes in controller and the Curve effect
Here is a Working example
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<SplashScreen>
with TickerProviderStateMixin {
AnimationController _controller;
Animation _animation;
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Color(0xFF005CA9),
body: Center(
child: AnimatedBuilder(
animation: _controller,
builder: (BuildContext context, Widget child) {
return Container(
child: Text(
'Hello World',
style: TextStyle(
color: Colors.white,
fontSize: _animation.value,
),
),
);
},
),
));
}
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 4));
_animation = Tween<double>(
begin: 20,
end: 35,
).animate(
CurvedAnimation(
parent: _controller,
curve: Curves.ease,
),
);
_controller.forward();
}
}
the code above produces the following output