问题
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,
),
),
),
],
),
),
);
}
}
回答1:
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,
),
),
),
),
),
);
}
}
回答2:
AnimatedContainer doesn't allow a width or height of null, which is a problem. Also you can't specify an alignment. Both is useful when dealing with text.
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;
AnimatedClipRect({
this.child,
this.open,
this.horizontalAnimation = true,
this.verticalAnimation = true,
this.alignment = Alignment.center,
this.duration,
this.reverseDuration,
this.curve = Curves.linear,
this.reverseCurve,
this.animationBehavior = AnimationBehavior.normal,
});
}
class _AnimatedClipRectState extends State<AnimatedClipRect> with TickerProviderStateMixin {
AnimationController _animationController;
Animation _animation;
@override
void initState() {
_animationController = AnimationController(
duration: widget.duration ?? const Duration(milliseconds: 500),
reverseDuration: widget.reverseDuration ?? (widget.duration ?? const Duration(milliseconds: 500)),
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: Duration(milliseconds: 1000),
curve: Curves.bounceOut,
reverseCurve: Curves.bounceIn,
child: Container(
color: Colors.lightGreenAccent,
padding: EdgeInsets.all(8),
child: 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.'),
),
),
RaisedButton(
child: 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.
来源:https://stackoverflow.com/questions/59132930/flutter-animatedsize-works-in-one-direction-only