I have got following code from https://medium.com/flutter-community/flutter-working-with-animatedsize-35253ff8f16a
it is using AnimatedSize, but animation works only while container is expanding and not while is it shirking. is this the default behavior? I want to animated while both expanding and shriking.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
double _height = 80.0;
double _width = 80.0;
var _color = Colors.blue;
bool _resized = false;
@override
Widget build(BuildContext context) {
return new Scaffold(
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedSize(
curve: Curves.easeIn,
vsync: this,
duration: new Duration(seconds: 1),
child: new GestureDetector(
onTap: () {
setState(() {
if (_resized) {
_resized = false;
_color = Colors.blue;
_height = 80.0;
_width = 80.0;
} else {
_resized = true;
_color = Colors.blue;
_height = 320.0;
_width = 320.0;
}
});
},
child: new Container(
width: _width,
height: _height,
color: _color,
),
),
),
],
),
),
);
}
}
AnimatedContainer isn't really an option when using text, lists or other variably sized widgets because it doesn't allow a width or height of null. Also it won't let you specify an alignment.
Instead I fixed this using a ClipRect with an animation controller. In the animation below you can see the word 'laboris' on the left of the center line in the closing bounce. This shows the text is getting centered during the closing of the container, just keep your eyes on 'laboris'.
I created an AnimatedClipRect widget class that you can easily implement yourself. You can specify alignment, curves, durations and if you want horizontal and/or vertical animation. As it is now it assumes you just want to fully close or open it:
class AnimatedClipRect extends StatefulWidget {
@override
_AnimatedClipRectState createState() => _AnimatedClipRectState();
final Widget child;
final bool open;
final bool horizontalAnimation;
final bool verticalAnimation;
final Alignment alignment;
final Duration duration;
final Duration? reverseDuration;
final Curve curve;
final Curve? reverseCurve;
///The behavior of the controller when [AccessibilityFeatures.disableAnimations] is true.
final AnimationBehavior animationBehavior;
const AnimatedClipRect({
Key? key,
required this.child,
required this.open,
this.horizontalAnimation = true,
this.verticalAnimation = true,
this.alignment = Alignment.center,
this.duration = const Duration(milliseconds: 500),
this.reverseDuration,
this.curve = Curves.linear,
this.reverseCurve,
this.animationBehavior = AnimationBehavior.normal,
}) : super(key: key);
}
class _AnimatedClipRectState extends State<AnimatedClipRect> with TickerProviderStateMixin {
late AnimationController _animationController;
late Animation _animation;
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
void initState() {
_animationController = AnimationController(
duration: widget.duration,
reverseDuration: widget.reverseDuration ?? widget.duration,
vsync: this,
value: widget.open ? 1.0 : 0.0,
animationBehavior: widget.animationBehavior);
_animation = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: _animationController,
curve: widget.curve,
reverseCurve: widget.reverseCurve ?? widget.curve,
));
super.initState();
}
@override
Widget build(BuildContext context) {
widget.open ? _animationController.forward() : _animationController.reverse();
return ClipRect(
child: AnimatedBuilder(
animation: _animationController,
builder: (_, child) {
return Align(
alignment: widget.alignment,
heightFactor: widget.verticalAnimation ? _animation.value : 1.0,
widthFactor: widget.horizontalAnimation ? _animation.value : 1.0,
child: child,
);
},
child: widget.child,
),
);
}
}
Basic usage example, put whatever you want to animate in its child:
// declare bool _open somewhere
Column(
children: <Widget>[
AnimatedClipRect(
open: _open,
horizontalAnimation: false,
verticalAnimation: true,
alignment: Alignment.center,
duration: const Duration(milliseconds: 1000),
curve: Curves.bounceOut,
reverseCurve: Curves.bounceIn,
child: Container(
color: Colors.lightGreenAccent,
padding: const EdgeInsets.all(8),
child: const Text(
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'),
),
),
ElevatedButton(
child: const Text("open/close"),
onPressed: () {
setState(() => _open ^= true);
}),
],
)
As you can see all you have to do is change _open and do setState((){}) to trigger the animation.
AnimatedContainer
is what you are looking for no need for AnimatedSize
.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> with TickerProviderStateMixin {
double _height = 80.0;
double _width = 80.0;
var _color = Colors.blue;
bool _resized = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
onTap: () {
setState(() {
if (_resized) {
_resized = false;
_color = Colors.blue;
_height = 80.0;
_width = 80.0;
} else {
_resized = true;
_color = Colors.blue;
_height = 320.0;
_width = 320.0;
}
});
},
child: AnimatedContainer(
duration: Duration(seconds: 1),
width: _width,
height: _height,
color: _color,
),
),
],
),
),
);
}
}
Or if you need AnimatedSize
you can add a new container with the color as the AnimatedSize
parent.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage>
with SingleTickerProviderStateMixin {
double _height = 320.0;
double _width = 320.0;
var _color = Colors.blue;
bool _resized = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: GestureDetector(
onTap: () {
setState(() {
if (_resized) {
_resized = false;
_color = Colors.blue;
_height = 80.0;
_width = 80.0;
} else {
_resized = true;
_color = Colors.blue;
_height = 320.0;
_width = 320.0;
}
});
},
child: Container(
color: _color,
child: AnimatedSize(
curve: Curves.easeIn,
vsync: this,
duration: Duration(seconds: 1),
child: Container(
width: _width,
height: _height,
),
),
),
),
),
);
}
}
Widget SizeTransition
worked for me. It works when the child height or width is null
too.
Here is my sample code:
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
static const String _title = 'Flutter Code Sample';
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: _title,
home: MyStatefulWidget(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget>
with TickerProviderStateMixin {
late final AnimationController _controller = AnimationController(
duration: const Duration(seconds: 1),
vsync: this,
);
late final Animation<double> _animation = CurvedAnimation(
parent: _controller,
curve: Curves.fastOutSlowIn,
);
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
SizeTransition(
sizeFactor: _animation,
axis: Axis.vertical,
axisAlignment: 1,
child: Container(
color: Colors.amberAccent,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: const [Text("Hello"), Text("World")],
),
),
),
TextButton(
child: const Text('toggle'),
onPressed: () => _controller.isDismissed
? _controller.forward()
: _controller.reverse(),
),
],
),
);
}
}
Animated Size was giving me the same problem as for you, but then I looked into AnimatedCrossFade's source code, and it used Animated Size and it animated size both while shrinking and expanding. Use LayoutBuilder as child of AnimatedSize and put the actual child inside LayoutBuilder. I'm not sure why it works, but that was how it was for AnimatedCrossFade.
I have noticed that AnimatedSize
fails in "one direction" when transitioning to a layout that contains RichText
. I was able to work around this simply by wrapping the text in a sized box with a fixed height (providing a height for the layout). If you are experiencing AnimatedSize
seemingly not working in random situations try adding fixed size constraints to your layout and see if it helps.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.