17 电子木鱼整理与总结

17 电子木鱼整理与总结

🪐
本文共 2843 字,阅读约 6 分钟。

通过上面 5 章的学习,我们已经完成了一个简单的电子木鱼小项目。这里将对项目在的一些知识点进行整理和总结,主要从 界面相关知识获得的技能点 两个方面进行总结:

image.png


一、 界面相关的知识

电子木鱼的界面交互要比猜数字要复杂很多,比如选择切换音效和图片、跳转历史记录界面。所以需要维护的数据以及组件的构建逻辑也更复杂,其中蕴含的知识也就更多。

1. 数据模型与界面展示

在界面中往往会有一些数据相关性很强,界面构建中需要很多份这样的数据。比如每个木鱼的样式,都有名称、资源、功德范围。这些数据就像一个个豌豆粒,一个个单独维护会非常复杂,把它们封装在一个类中,就相当于让它们住进一个豌豆皮中。

image.png

所以这种对若干个数据进行封装的类型也被形象地称之为 Bean,也可以称之为 数据模型 Model 。比如这里的 ImageOption 类型就是对木鱼样式数据的封装,一个 ImageOption 对象就对应着界面展示中的一个图片选项。

class ImageOption{
  final String name; // 名称
  final String src; // 资源
  final int min; // 每次点击时功德最小值
  final int max; // 每次点击时功德最大值

  const ImageOption(this.name, this.src, this.min, this.max);
}

2. 组件的封装性

还拿木鱼选择的界面来说,可以看出两个木鱼选项的界面结构是完全一样的,只不过是内容数据信息的不同。这时并没有必要写两份构建逻辑,通过一个组件进行封装,界面构建这依赖的数据通过构造中传入。比如这里依赖 ImageOption 和是否激活两个数据:

image.png

封装数据模型类之后,在传参时也更加方便,否则就需要传入一个个的豌豆粒。在构建逻辑中就可以根据传入的数据,构建对应的组件,完成界面展示的任务。

image.png

其中 ImageOptionItem 组件也像一个豌豆,里面的豌豆粒就是构建过程中使用的组件。豌豆把豌豆粒包裹在一块,这就是一种现实中的 封装 。为 ImageOptionItem 组件提供不同的 ImageOption,就可以展示不同的界面。所以这里只需要准备份数据就行了,而不是重复写两次构建逻辑。

这就是封装的一大特点: 可复用性。

复用的目标就是封装的内容,对于组件来说,封装的目标就是构建逻辑。

ImageOptionItem(option: option1, active: true),
ImageOptionItem(option: option2, active: false),

可复用性往往会让使用变得简单,比如 Text 组件是源码提供的组件,它封装了文本渲染的过程。对于使用者来说,只要创建 Text 对象,并不需要理解底层的渲染原理。如果 ImageOptionItem 组件是 A 同学写的,那么把它发给 B 同学,那 B 同学只需要准备数据即可。所以,逻辑一旦封装,也会有 普适性 : 一人封装,万人可用,这也是类库生态的基石。


3. State 状态类的生命周期回调

State 的生命周期回调是非常重要的,但对于初学者来说,目前只能先了解这些回调的作用和使用方式。

  • initState 回调方法会在 State 对象创建后被触发一次,可以在其中处理成员对象的初始化逻辑。比如动画控制器创建、音频播放器对象的创建等:

1683987456233.png

  • dispose 回调方法会在 State 对象销毁前触发一次,用于释放一些资源,比如动画控制器、文字控制器、音频播放器等的销毁工作。

image.png

  • build 回调方法返回 Widget 对象,也就是组件的构建逻辑。该方法可能在一个 State 对象生命之中可能触发多次,通过 State#setState 方法可以导致 build 方法重新执行。

image.png

  • didUpdateWidget 会在 State 对应的组件发生变化时被触发;它在一个 State 对象生命之中也可能触发多次。最常见的场合是上层状态类触发 setState,导致组件发生变化,该回调中可以访问到旧的组件,通常会根据新旧组件的配置属性来触发事件,或更新一些状态类内部数据。

image.png

现在只需要会用这四个回调即可,想要完全把握 State 状态类的生命周期回调,需要从源码的角度去理解回调触发的流程。希望之后,你可以在后续的路途上,通过 《Flutter 渲染机制 - 聚沙成塔》 找到满意的答案。


4. 界面的跳转

MaterialApp 的内部集成了一套路由体系,在代码中只通过 Navigator.of(context) 来获取 NavigatorState 对象,该对象的 push 方法实现可以跳转界面的功能。下面代码中 MaterialPageRoute 是一个默认的跳转动画界面路由,其中的 builder 方法用来构建跳转到的界面:

Navigator.of(context).push(
  MaterialPageRoute(
    builder: (_) => RecordHistory(
      records: _records.reversed.toList(),
    ),
  ),
);

二、 技能点

在电子木鱼项目中,涉及到了资源的引入,以及使用依赖库。依赖库的存在,极大方便了开发者。你可以轻松地获取和使用别人封装好的代码,来为自己的项目服务,比如这里的音频播放器、uuid 的获取以及日期时间的格式化。

1. 资源配置与依赖库的使用

Flutter 项目的配置文件是 pubspec.yaml ,其中依赖库配置在 dependencies 节点下。配置之后记得添加一右上角的 pub get 按钮获取依赖,或者在根目录下的命令行中输入:

flutter pub get

image.png

获取依赖之后,就可以导入包中的文件,然后使用其中的类或方法:

import 'package:flame_audio/flame_audio.dart';

图片、文本等本地资源需要在 flutter 节点下的 asstes 下配置,一般习惯于将资源文件放在项目根目录的 assets 文件夹中。配置时只需要写文件夹,就可以访问期内的所有文件:

image.png


2. 多种动画变换

木鱼在点击时,当前功德文字会以 缩放+移动+透明度 动画变化,代码中通过多个 XXXTransition 嵌套实现的。除了这三种,还有些其他的 Transition ,在使用时都需要使用者提供相关类型的动画器:

image.png

这里想强调的是 : 基于一个动画控制器,可以通过 Tween 来生成其他类型的动画器 Animation ,它们共用一个动力源。也就是说 controller 启动时,这些动画器都会进行动画变化:

image.png


3. 短音效的播放

flame_audio 插件可以实现音频播放的功能,使用起来也非常简单:通过 FlameAudio.createPool 静态方法,指定资源创建 AudioPool ,执行 start 即可。

AudioPool pool = await FlameAudio.createPool(src, maxPlayers: 1);
pool.start();

Flutter 中音频播放还有一个 audioplayers 库,其实 flame_audio 是对该库的一个上层封装,引入了缓存池,在播放短音效的场景中更好一些。我也试过 audioplayers ,感觉连续点击短音效有一点杂音,而 flame_audio 表现良好。


4. 唯一标识 uuid

有些时候,我们需要用一个成员属性来标识对象的唯一性, 一般应用通过 uuid 获取标识符可以视为唯一的。这里使用 uuid 的目的是为每次功德的增加记录提供一个身份标识。

final Uuid uuid = Uuid();
String id = uuid.v4();

得益于 Flutter 的良好生态环境,像这些基础功能,都已经有了依赖库,所以两行代码就能完成需求。以后有通用的能需要求,可以在 pub 中看一下有没有依赖库。当然,你有好的功能代码,也可以创建依赖库,提交到 pub 中,供大家一起使用。


三、接触的内置组件

最后来整理一下目前电子木鱼项目中用到的 Flutter 内置组件,大家可以根据下表,结合源码以及应用界面,思考一下这些组件的作用和使用方式:

1. 基础组件
组件名称功能猜数字中的使用
Image图片展示展示木鱼图片
GestureDetector手势事件监听器监听点击图片事件,处理敲击逻辑
ElevatedButton升起按钮左上角选择音频和木鱼样式和按钮
ScaleTransition缩放变换功德数字缩放动画
SlideTransition移动变换功德数字移动动画
FadeTransition透明度变换功德数字透明度动画
Material材料组件为选择界面提供材料设计默认主题

2. 组合结构型
组件名称功能猜数字中的使用
ListTile列表条目通用结构音频选项以及列表条目

3. 布局组件
组件名称功能猜数字中的使用
ExpandedRow/Column 中延展区域主页面上下平分区域
PositionedStack 中定位组件左上角按钮的定位
Wrap包裹若干组件左上角两个按钮的竖直包裹
Padding设置内边距木鱼样式选择界面中的布局
SizedBox尺寸设置选择界面的高度设置
ListView滑动列表历史记录界面

最后想说一下关于木鱼项目的演变:当前项目的功能有点击发声、切换图片、数据记录。其实可以基于此完成另一个小项目,比如电子宠物饲养类型的应用,可以喂食,抚摸发出叫声,切换宠物等操作。这和点击敲木鱼是殊途同归的,有时间和兴趣的可以自己尝试一下。