首次加载列中的小部件的 Flutter 浮动和淡入动画
Flutter Float and Fade In Animation for Widgets in Column with Loading for the First Time
我正在尝试创建动画屏幕,例如,在加载屏幕时,每个屏幕都会包含一个带有小部件的列。我想要做的是,当加载屏幕时,让列中的每个小部件按照它们出现的顺序从屏幕外(从底部)浮动。
我一直在搜索,但似乎找不到解决方案。我怎样才能做到这一点?
我做了一些可能是你想要的事情。然而,为了让每个元素在最后一个元素之后为自己而不是全部在一起设置动画,我必须使用 ListView.builder
来获取 index
。如果可以找到元素的 index
,则可以使用 Column
小部件。
代码如下:
主屏幕:
import 'package:flutter/material.dart';
import 'package:testing/fade_in_from_bottom.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
final List<Widget> children = <Widget>[
Container(
height: 32.0,
color: Colors.amber,
),
Container(
height: 32.0,
color: Colors.black,
),
Container(
height: 32.0,
color: Colors.purple,
),
Container(
height: 32.0,
color: Colors.green,
),
Container(
height: 32.0,
color: Colors.indigo,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: children.length,
itemBuilder: (BuildContext context, int index) {
return FadeInFromBottom(
key: UniqueKey(), // this is very important
index: index,
child: children[index],
);
},
),
),
],
),
);
}
}
这是我制作的 FadeInFromBottom
小部件:
import 'package:flutter/material.dart';
class FadeInFromBottom extends StatefulWidget {
@override
_FadeInFromBottomState createState() => _FadeInFromBottomState();
final Key key;
final Duration animationDuration;
final Duration offsetDuration;
final Widget child;
final int index;
FadeInFromBottom({
@required this.key,
@required this.child,
@required this.index,
this.animationDuration = const Duration(milliseconds: 400),
this.offsetDuration = const Duration(milliseconds: 800),
}) : super(key: key); // this line is important
}
// How to add AutomaticKeepAliveClientMixin? Follow steps 1, 2 and 3:
// 1. add AutomaticKeepAliveClientMixin to FadeInFromBottom widget State
class _FadeInFromBottomState extends State<FadeInFromBottom>
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
bool get wantKeepAlive => true; // 2. add this line
double progress = 0.0;
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
final Duration offsetDuration =
widget.offsetDuration == null ? 0.0 : widget.offsetDuration;
final int index = widget.index == null ? 0 : widget.index;
// we await the future to create the animation delay
Future.delayed(offsetDuration * index).then(
(_) {
controller = AnimationController(
duration: widget.animationDuration, vsync: this);
animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Curves.linear,
),
)..addListener(() {
setState(() => progress = animation.value);
});
controller.forward();
},
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context); // 3. add this line
return Opacity(
opacity: progress,
child: Transform.translate(
offset: Offset(
0.0,
(1.0 - progress) * 999.0,
),
child: widget.child,
),
);
}
}
不要忘记添加 key: UniqueKey()
属性,没有它动画会乱七八糟。
我正在尝试创建动画屏幕,例如,在加载屏幕时,每个屏幕都会包含一个带有小部件的列。我想要做的是,当加载屏幕时,让列中的每个小部件按照它们出现的顺序从屏幕外(从底部)浮动。
我一直在搜索,但似乎找不到解决方案。我怎样才能做到这一点?
我做了一些可能是你想要的事情。然而,为了让每个元素在最后一个元素之后为自己而不是全部在一起设置动画,我必须使用 ListView.builder
来获取 index
。如果可以找到元素的 index
,则可以使用 Column
小部件。
代码如下:
主屏幕:
import 'package:flutter/material.dart';
import 'package:testing/fade_in_from_bottom.dart';
void main() => runApp(App());
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: HomePage(),
);
}
}
class HomePage extends StatelessWidget {
final List<Widget> children = <Widget>[
Container(
height: 32.0,
color: Colors.amber,
),
Container(
height: 32.0,
color: Colors.black,
),
Container(
height: 32.0,
color: Colors.purple,
),
Container(
height: 32.0,
color: Colors.green,
),
Container(
height: 32.0,
color: Colors.indigo,
),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: ListView.builder(
itemCount: children.length,
itemBuilder: (BuildContext context, int index) {
return FadeInFromBottom(
key: UniqueKey(), // this is very important
index: index,
child: children[index],
);
},
),
),
],
),
);
}
}
这是我制作的 FadeInFromBottom
小部件:
import 'package:flutter/material.dart';
class FadeInFromBottom extends StatefulWidget {
@override
_FadeInFromBottomState createState() => _FadeInFromBottomState();
final Key key;
final Duration animationDuration;
final Duration offsetDuration;
final Widget child;
final int index;
FadeInFromBottom({
@required this.key,
@required this.child,
@required this.index,
this.animationDuration = const Duration(milliseconds: 400),
this.offsetDuration = const Duration(milliseconds: 800),
}) : super(key: key); // this line is important
}
// How to add AutomaticKeepAliveClientMixin? Follow steps 1, 2 and 3:
// 1. add AutomaticKeepAliveClientMixin to FadeInFromBottom widget State
class _FadeInFromBottomState extends State<FadeInFromBottom>
with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
bool get wantKeepAlive => true; // 2. add this line
double progress = 0.0;
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
final Duration offsetDuration =
widget.offsetDuration == null ? 0.0 : widget.offsetDuration;
final int index = widget.index == null ? 0 : widget.index;
// we await the future to create the animation delay
Future.delayed(offsetDuration * index).then(
(_) {
controller = AnimationController(
duration: widget.animationDuration, vsync: this);
animation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: controller,
curve: Curves.linear,
),
)..addListener(() {
setState(() => progress = animation.value);
});
controller.forward();
},
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
super.build(context); // 3. add this line
return Opacity(
opacity: progress,
child: Transform.translate(
offset: Offset(
0.0,
(1.0 - progress) * 999.0,
),
child: widget.child,
),
);
}
}
不要忘记添加 key: UniqueKey()
属性,没有它动画会乱七八糟。