跳到主要内容

flutter如何实现图标切换动画

本文是Flutter动画系列的第八篇,建议读者阅读前面的教程,做到无缝衔接。

 当按钮被点击时,图标也需要相应的变化,例如播放——暂停这种场景。本文介绍如何在 flutter 里实现图标切换的动画。

图标切换效果

1. AnimatedIcon

flutter 提供了AnimatedIcon类来实现图标的切换,它的构造函数如下,其中icon表示需要切换的图标,本文使用的是AnimatedIcons.play_pause,即播放和暂停组合;progress为动画参数。

AnimatedIcon({Key? key, required AnimatedIconData icon, required Animation<double> progress, Color? color, double? size, String? semanticLabel, TextDirection? textDirection})

 完整的代码如下

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 AnimationController controller;

@override
void initState() {
super.initState();
controller =
AnimationController(vsync: this, duration: const Duration(seconds: 1));
}

@override
void dispose() {
controller.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("app"),
),
body: const Center(),
floatingActionButton: FloatingActionButton(
onPressed: () {
if (controller.isDismissed) {
controller.forward();
} else {
controller.reverse();
}
},
child:
AnimatedIcon(icon: AnimatedIcons.play_pause, progress: controller),
),
);
}
}

2. AnimatedCrossFade

AnimatedIcons提供的组合比较有限,有时无法满足需求。同时 flutter 没有提供类似ColorTween这样的类来实现图标切换,所以我们只能借助其他的类。

AnimatedCrossFadeflutter 提供的动画类,它的构造函数如下。AnimatedCrossFade用来实现两个子组件间的切换动画,firstChildsecondChild分别指两个子组件,只要指定这两个组件为图标,就可以实现图标切换的动画。

AnimatedCrossFade({Key? key, required Widget firstChild, required Widget secondChild, Curve firstCurve = Curves.linear, Curve secondCurve = Curves.linear, Curve sizeCurve = Curves.linear, AlignmentGeometry alignment = Alignment.topCenter, required CrossFadeState crossFadeState, required Duration duration, Duration? reverseDuration, AnimatedCrossFadeBuilder layoutBuilder = defaultLayoutBuilder, bool excludeBottomFocus = true})

crossFadeState用来表示动画结束时,展示哪个子组件。在AnimatedCrossFade重新渲染时,它会判断crossFadeState是否发生变化,如果发生变化就会触发动画。所以我们只需要控制crossFadeState即可,完整的代码如下。

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 {
bool first = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("app"),
),
body: const Center(),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
first = !first;
});
},
child: AnimatedCrossFade(
firstChild: const Icon(Icons.ac_unit),
secondChild: const Icon(Icons.access_alarm),
crossFadeState:
first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
duration: const Duration(seconds: 1)),
),
);
}
}

AnimatedCrossFade的参数还有很多,它支持设置第一个子组件消失时的 curve,也支持第二个组件消失时的 curve 等,感兴趣的读者可以自行尝试。

3. AnimatedSwitcher

AnimatedSwitcherAnimatedCrossFade非常类似,当AnimatedSwitcher的子组件发生变化时会使用动画进行过渡。AnimatedSwitcher的构造函数如下,child表示它的子组件,当AnimatedSwitcher重新渲染时,如果子组件发生变化时,会使用 cross-fade 进行过渡,当然用户可以通过参数transitionBuilder修改过渡方式。

AnimatedSwitcher({Key? key, Widget? child, required Duration duration, Duration? reverseDuration, Curve switchInCurve = Curves.linear, Curve switchOutCurve = Curves.linear, AnimatedSwitcherTransitionBuilder transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, AnimatedSwitcherLayoutBuilder layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder})

 完整的代码如下,需要注意的是,AnimatedSwitcher通过子组件的 key 来判断组件是否发生变化。

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 {
bool first = true;

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("app"),
),
body: const Center(),
floatingActionButton: FloatingActionButton(
onPressed: () {
setState(() {
first = !first;
});
},
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
reverseDuration: const Duration(seconds: 5),
child: first
? const Icon(
Icons.play_arrow,
key: Key("play_icon"),
)
: const Icon(
Icons.stop_circle,
key: Key("stop_icon"),
),
),
),
);
}
}

署名-非商业性使用-禁止演绎 4.0 国际