learn-tech/专栏/Flutter入门教程/14计数变化与音效播放.md
2024-10-16 00:01:16 +08:00

322 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

因收到Google相关通知网站将会择期关闭。相关通知内容
14 计数变化与音效播放
1. 组件点击事件的监听
计时器项目中的 FloatingActionButton 按钮,猜数字项目中的 IconButton 点击事件,都是通过构造中的 onPressed 参数传入函数,执行相关逻辑。我们称这种以函数对象作为参数的形式为 回调函数 。现在我们的需求是:让图片可以响应点击事件,每次点击时功德数随机增加 1~3 点,并发出敲击的音效。
————————————————————
————————————————————
如下所示,在 MuyuAssetsImage 中使用 GestureDetector 嵌套在 Image 之上,这样图片区域内的点击事件,可以通过 onTap 回调监听到。另外,点击时功德累加的逻辑,和图片构建并没有太大关系,这里通过 onTap 入参,将事件向上级传递,交由使用者处理。
class MuyuAssetsImage extends StatelessWidget {
final String image;
final VoidCallback onTap;
const MuyuAssetsImage({super.key, required this.image, required this.onTap});
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector( // 使用 GestureDetector 组件监听手势回调
onTap: onTap,
child: Image.asset(
image,
height: 200,
),
),
);
}
}
通过 GestureDetector 组件的 onTap 参数,可以监听到任何组件在其区域内的点击事件。
在 _MuyuPageState#build 方法中,使用 MuyuAssetsImage 组件时,通过 onTap 参数指定逻辑处理函数,这和 FloatingActionButton的onPressed 点击事件本质上是一样的。这里通过 _onKnock 函数处理点击木鱼的逻辑:
---->[_MuyuPageState#build]----
Expanded(
child: MuyuAssetsImage(
image: 'assets/images/muyu.png',
onTap: _onKnock,
),
),
在 _MuyuPageState 中维护界面中需要的状态数据,这里最主要的是功德计数变量,通过 _counter 进行表示。另外,点击时功德数随机增加 1~3 ,需要随机数对象。这样,目前的逻辑就比较清晰了:在 _onKnock 函数中,对 _counter 进行累加即可,每次累加值为 1 + _random.nextInt(3) :
int _counter = 0;
final Random _random = Random();
void _onKnock() {
setState(() {
int addCount = 1 + _random.nextInt(3);
_counter += addCount;
});
}
最后,将 _counter 作为上半界面 CountPanel 的入参即可。这样点击时,更新 _counter 后,会重新构建,从而展示最新的数据。到这里本质上和计数器没有太大的差异:
当前代码位置 muyu
2. 插件的使用和配置
Flutter 是一个跨平台的 UI 框架,而音效的播放是平台的功能。在开发过程中,一般使用 插件 来完成平台功能。得益于 Flutter 良好的生态环境,在 pub 中有丰富的优秀插件和包。这里使用 flame_audio 插件来实现短音效播放的功能:
使用插件,首先要在 pubspec.yaml 的 dependencies 节点下配置依赖,在 pub 中的 installing 页签中,可以看到依赖的版本号:
如下所示,加入依赖后,点击 pub get 获取依赖包:
现在要播放音效,首先需要相关的音频资源,而使用本地资源,需要在 pubspec.yaml 中进行配置:这里准备了三个木鱼点击的音效,放在 audio 文件夹中。
3. 完成点击播放音效功能
点击事件的逻辑处理在 _MuyuPageState 类中,所以播放音效在该类中处理比较方便。插件的使用也比较简单,首先需要加载资源,通过 FlameAudio.createPool 方法创建 AudioPool 对象。在状态类的 initState 方法中使用 _initAudioPool 创建 pool 对象。
其中第一个参数是资源的名称flame_audio 默认将本地资源放在 assets/audio 中,指定资源时直接写文件名即可。
---->[_MuyuPageState]----
AudioPool? pool;
@override
void initState() {
super.initState();
_initAudioPool();
}
void _initAudioPool() async {
pool = await FlameAudio.createPool(
'muyu_1.mp3',
maxPlayers: 4,
);
}
然后,只需要在 _onKnock 方法在调用 pool 的 start 方法进行播放即可:
void _onKnock() {
pool?.start();
// 略同...
}
到这里,点击木鱼时,就可以听到敲击木鱼的音效,同时功德数也会随机增加 1~3 点。这样就完成了木鱼最基础的功能需求,下面我们将继续优化和拓展,添加一些新的视觉表现和功能。
当前代码位置 muyu
4. 增加功德时的动画展示
如下所示,现在需要在每次点击时展示功德增加的数字,并让数值有动画效果。比如这里是 缩放、移动、透明度 三个动画的叠加,从效果的表现上来说就是文字一边上移,一边缩小、一边透明度降低。
————————————————————
————————————————————
首先来分析一下界面构建:界面上需要展示的信息增加了 当前增加的功德值 ,使用需要增加一个状态数据用于表示。这里使用 _cruValue 变量,该数据维护的时机也很清晰,在点击时更新 _cruValue 的值:
---->[_MuyuPageState]----
int _cruValue = 0;
void _onKnock() {
pool?.start();
setState(() {
_cruValue = 1 + _random.nextInt(3);
_counter += _cruValue;
});
}
这样数据层面就搞定了,下面看一下界面构建逻辑。这里 当前功德 相当于一个浮层,可以通过 Stack 组件和下半部分进行叠放。如下所示,文字从下向上运动到下半部分的顶部:
由于动画的逻辑比较复杂,这里封装 AnimateText 组件来完成动画文字的功能,其中构造函数传入需要展示的文本信息。下半部分通过 Stack 进行叠放alignment 入参可以控制孩子们的对齐方式,比如这里 Alignment.topCenter 将会以上方中心对齐。表现上来说,就是文字在区域的上方中间:
现在数据和布局已经完成,就差动画效果了。在猜数字中我们简单地了解过动画的使用,通过 AnimatedBuilder 组件,监听动画控制器的变化,在构建组件的过程中让某些属性值不断变化。这里介绍一下几个 XXXTransition 动画组件的使用。
先拿 FadeTransition 组件来说,它的构造中需要传入 Animation<double> 的动画器表示透明度的动画变化。AnimationController 的数值运动是从 0 ~ 1 ,而这里透明度的变化是 1 ~ 0此时可以使用 Tween 来得到期望的补间动画器。如下 tag1 处,创建了 1~0 变化的动画器 opacity 。在 build 中,使用 FadeTransition 传入 opacity 动画器,当动画控制器运动时,子组件就会产生透明度动画。
————————————————————
————————————————————
class AnimateText extends StatefulWidget {
final String text;
const AnimateText({Key? key, required this.text}) : super(key: key);
@override
State<AnimateText> createState() => _FadTextState();
}
class _FadTextState extends State<AnimateText> with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation<double> opacity;
@override
void initState() {
super.initState();
controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 500));
opacity = Tween(begin: 1.0, end: 0.0).animate(controller); // tag1
controller.forward();
}
@override
void didUpdateWidget(covariant AnimateText oldWidget) {
super.didUpdateWidget(oldWidget);
controller.forward(from: 0);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: opacity,
child: Text(widget.text),
);
}
}
当前代码位置 muyu
同理,使用 ScaleTransition 可以实现缩放动画;使用 SlideTransition 可以实现移动动:
@override
Widget build(BuildContext context) {
return ScaleTransition(
scale: scale,
child: SlideTransition(
position: position,
child: FadeTransition(
opacity: opacity,
child: Text(widget.text),
)),
);
}
XXXTransition 组件都需要指定对应类型的 Animation 动画器,而这些动画器可以以 AnimationController 为动力源,通过 Tween 来生成。 缩放变换传入 scale 动画器,从 1 ~ 0.9 变化;移动变化传入 position 动画器,泛型为 Offset从 Offset(0, 2) ~ Offset.zero 变化,对应的效果就是:在竖直方向上的偏移量,从两倍子组件高度变化到 0 。
————————————————————
————————————————————
class _FadTextState extends State<AnimateText> with SingleTickerProviderStateMixin {
late AnimationController controller;
late Animation<double> opacity;
late Animation<Offset> position;
late Animation<double> scale;
@override
void initState() {
super.initState();
controller = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
opacity = Tween(begin: 1.0, end: 0.0).animate(controller);
scale = Tween(begin: 1.0, end: 0.9).animate(controller);
position = Tween<Offset>(begin: const Offset(0, 2), end: Offset.zero,).animate(controller);
controller.forward();
}
5.本章小结
本章我们学习了如何在自己的项目中使用别人提供的类库,这样就可以很轻松地完成复杂的功能。比如这里点击时的音效播放,如果完全靠自己来写代码实现,将会非常困难。但使用别人提供的插件,几行代码就搞定了。所以说,对于一项技术而言,良好的生态是非常重要的。
你可以使用别人的代码实现功能,方便大家;也可以分享自己的代码以供别人使用,让大家一起完善,这就是开源的价值。到这里,就完成了增加功德数动画的展示效果,当前代码位置 muyu 。下一篇将继续对当前项目进行功能拓展,增加切换音效和木鱼图片的效果。