前言
前段时间终于把Getwidget系列组件介绍完毕了,今天打开Nike看着里面的启动载入动画突然灵机一动,我好像可以用Getwidget轻松的写一个出来,话不多说,开整。
准备工作
需要准备一张高清无码的耐克Logo,推荐一个我常用的矢量图下载网站(切记商用要注意版权问题)。
新建项目,导入图片、集成Getwidget。
开始写代码
分析动画
通过观察发现整体大概有以下要点:
- 整体背景为黑色,logo及加载指示器为白色。
- 开屏后首先展示的是一个logo处于屏幕的正中间,初始宽度大约为屏幕宽度的1/3。
- 经过大概1秒的静止后后logo开始变小,缩放比率大约是0.4,动画时间大约为0.3s。
- 缩放动画结束时启动外圈的加载指示器。
开始布局
1.为了方便观察,我这里使用了GFBorder
先将大概的动画区域圈了出来。
class NikeScreenView extends StatefulWidget {
const NikeScreenView({Key? key}) : super(key: key);
@override
State<NikeScreenView> createState() => _NikeScreenViewState();
}
class _NikeScreenViewState extends State<NikeScreenView> {
@override
Widget build(BuildContext context) {
double size = MediaQuery.of(context).size.width / 3;
return Container(
color: Colors.black,
child: Center(
child: GFBorder(
color: GFColors.LIGHT,
padding: const EdgeInsets.all(0),
type: GFBorderType.rect,
child: SizedBox(
width: size,
height: size,
),
),
),
);
}
}
2.使用Getwidget中的动画容器GFAnimation
实现logo动画部分:
...
class _NikeScreenViewState extends State<NikeScreenView>
with TickerProviderStateMixin {
late AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 300),
lowerBound: 0.4,
upperBound: 1.0,
// 因为是缩小动画,所以设置初始值为1.0
value: 1.0,
vsync: this);
late Animation<double> animation = CurvedAnimation(
parent: controller,
curve: Curves.linear);
@override
void initState() {
// 1秒后开始执行动画,无需延迟则设置为0
Future.delayed(const Duration(milliseconds: 1000), () {
controller.reverse();
});
super.initState();
}
// 别忘了界面销毁时释放控制器
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
...
// 设置stack为sizedbox的子组件,用于盛放logo及加载器
Stack(
alignment: Alignment.center,
children: [
GFAnimation(
scaleAnimation: animation,
controller: controller,
type: GFAnimationType.scaleTransition,
alignment: Alignment.center,
child: Image.asset(
'images/nike_logo.png',
),
),
],
),
...
3.使用GFLoader
的android样式来处理转圈圈的指示器,但你会发现它并不能直接设置颜色,别急,点开GFLoader
会发现其实它内部调用的是CircularProgressIndicator
,这是material自带的组件,设置宽度、颜色,然后监听logo动画的状态,在缩放结束时展示加载器。
(写到这里豁然发现完全没必要用Getwidget,靓仔落泪...)
...
bool isShowLoader = false;
@override
void initState() {
Future.delayed(const Duration(milliseconds: 500), () {
controller.reverse();
});
// 监听动画执行结束,展示Loader
controller.addStatusListener((status) {
if (status == AnimationStatus.dismissed) {
setState(() {
isShowLoader = true;
});
}
});
super.initState();
}
...
Stack(
alignment: Alignment.center,
children: [
...
isShowLoader ? SizedBox(
width: size * 0.7,
height: size * 0.7,
child: const CircularProgressIndicator(
color: Colors.white,
strokeWidth: 2,
semanticsValue: 'abc',
),
)
: const SizedBox(),
])
...
4.最后去掉GFBorder看下效果:
结语
至此,一个仿Nike的加载页就完成了。 Flutter启动白屏处理请戳