通过上面 5 章的学习,我们已经完成了一个简单的电子木鱼小项目。这里将对项目在的一些知识点进行整理和总结,主要从 界面相关知识 和 获得的技能点 两个方面进行总结:
电子木鱼的界面交互要比猜数字要复杂很多,比如选择切换音效和图片、跳转历史记录界面。所以需要维护的数据以及组件的构建逻辑也更复杂,其中蕴含的知识也就更多。
在界面中往往会有一些数据相关性很强,界面构建中需要很多份这样的数据。比如每个木鱼的样式,都有名称、资源、功德范围。这些数据就像一个个豌豆粒,一个个单独维护会非常复杂,把它们封装在一个类中,就相当于让它们住进一个豌豆皮中。
所以这种对若干个数据进行封装的类型也被形象地称之为 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);
}
还拿木鱼选择的界面来说,可以看出两个木鱼选项的界面结构是完全一样的,只不过是内容数据信息的不同。这时并没有必要写两份构建逻辑,通过一个组件进行封装,界面构建这依赖的数据通过构造中传入。比如这里依赖 ImageOption
和是否激活两个数据:
封装数据模型类之后,在传参时也更加方便,否则就需要传入一个个的豌豆粒。在构建逻辑中就可以根据传入的数据,构建对应的组件,完成界面展示的任务。
其中 ImageOptionItem
组件也像一个豌豆,里面的豌豆粒就是构建过程中使用的组件。豌豆把豌豆粒包裹在一块,这就是一种现实中的 封装 。为 ImageOptionItem
组件提供不同的 ImageOption
,就可以展示不同的界面。所以这里只需要准备份数据就行了,而不是重复写两次构建逻辑。
这就是封装的一大特点: 可复用性。
复用的目标就是封装的内容,对于组件来说,封装的目标就是构建逻辑。
ImageOptionItem(option: option1, active: true),
ImageOptionItem(option: option2, active: false),
可复用性往往会让使用变得简单,比如 Text 组件是源码提供的组件,它封装了文本渲染的过程。对于使用者来说,只要创建 Text 对象,并不需要理解底层的渲染原理。如果 ImageOptionItem 组件是 A 同学写的,那么把它发给 B 同学,那 B 同学只需要准备数据即可。所以,逻辑一旦封装,也会有 普适性 : 一人封装,万人可用,这也是类库生态的基石。
State 的生命周期回调是非常重要的,但对于初学者来说,目前只能先了解这些回调的作用和使用方式。
State#setState
方法可以导致 build 方法重新执行。现在只需要会用这四个回调即可,想要完全把握 State 状态类的生命周期回调,需要从源码的角度去理解回调触发的流程。希望之后,你可以在后续的路途上,通过 《Flutter 渲染机制 - 聚沙成塔》 找到满意的答案。
MaterialApp 的内部集成了一套路由体系,在代码中只通过 Navigator.of(context) 来获取 NavigatorState
对象,该对象的 push
方法实现可以跳转界面的功能。下面代码中 MaterialPageRoute
是一个默认的跳转动画界面路由,其中的 builder
方法用来构建跳转到的界面:
Navigator.of(context).push(
MaterialPageRoute(
builder: (_) => RecordHistory(
records: _records.reversed.toList(),
),
),
);
在电子木鱼项目中,涉及到了资源的引入,以及使用依赖库。依赖库的存在,极大方便了开发者。你可以轻松地获取和使用别人封装好的代码,来为自己的项目服务,比如这里的音频播放器、uuid 的获取以及日期时间的格式化。
Flutter 项目的配置文件是 pubspec.yaml
,其中依赖库配置在 dependencies
节点下。配置之后记得添加一右上角的 pub get 按钮获取依赖,或者在根目录下的命令行中输入:
flutter pub get
获取依赖之后,就可以导入包中的文件,然后使用其中的类或方法:
import 'package:flame_audio/flame_audio.dart';
图片、文本等本地资源需要在 flutter 节点下的 asstes
下配置,一般习惯于将资源文件放在项目根目录的 assets
文件夹中。配置时只需要写文件夹,就可以访问期内的所有文件:
木鱼在点击时,当前功德文字会以 缩放+移动+透明度 动画变化,代码中通过多个 XXXTransition
嵌套实现的。除了这三种,还有些其他的 Transition
,在使用时都需要使用者提供相关类型的动画器:
这里想强调的是 : 基于一个动画控制器,可以通过 Tween
来生成其他类型的动画器 Animation ,它们共用一个动力源。也就是说 controller
启动时,这些动画器都会进行动画变化:
flame_audio 插件可以实现音频播放的功能,使用起来也非常简单:通过 FlameAudio.createPool
静态方法,指定资源创建 AudioPool
,执行 start
即可。
AudioPool pool = await FlameAudio.createPool(src, maxPlayers: 1);
pool.start();
Flutter 中音频播放还有一个 audioplayers 库,其实 flame_audio
是对该库的一个上层封装,引入了缓存池,在播放短音效的场景中更好一些。我也试过 audioplayers ,感觉连续点击短音效有一点杂音,而 flame_audio 表现良好。
有些时候,我们需要用一个成员属性来标识对象的唯一性, 一般应用通过 uuid 获取标识符可以视为唯一的。这里使用 uuid
的目的是为每次功德的增加记录提供一个身份标识。
final Uuid uuid = Uuid();
String id = uuid.v4();
得益于 Flutter 的良好生态环境,像这些基础功能,都已经有了依赖库,所以两行代码就能完成需求。以后有通用的能需要求,可以在 pub 中看一下有没有依赖库。当然,你有好的功能代码,也可以创建依赖库,提交到 pub 中,供大家一起使用。
最后来整理一下目前电子木鱼项目中用到的 Flutter 内置组件,大家可以根据下表,结合源码以及应用界面,思考一下这些组件的作用和使用方式:
组件名称 | 功能 | 猜数字中的使用 |
---|---|---|
Image | 图片展示 | 展示木鱼图片 |
GestureDetector | 手势事件监听器 | 监听点击图片事件,处理敲击逻辑 |
ElevatedButton | 升起按钮 | 左上角选择音频和木鱼样式和按钮 |
ScaleTransition | 缩放变换 | 功德数字缩放动画 |
SlideTransition | 移动变换 | 功德数字移动动画 |
FadeTransition | 透明度变换 | 功德数字透明度动画 |
Material | 材料组件 | 为选择界面提供材料设计默认主题 |
组件名称 | 功能 | 猜数字中的使用 |
---|---|---|
ListTile | 列表条目通用结构 | 音频选项以及列表条目 |
组件名称 | 功能 | 猜数字中的使用 |
---|---|---|
Expanded | Row/Column 中延展区域 | 主页面上下平分区域 |
Positioned | Stack 中定位组件 | 左上角按钮的定位 |
Wrap | 包裹若干组件 | 左上角两个按钮的竖直包裹 |
Padding | 设置内边距 | 木鱼样式选择界面中的布局 |
SizedBox | 尺寸设置 | 选择界面的高度设置 |
ListView | 滑动列表 | 历史记录界面 |
最后想说一下关于木鱼项目的演变:当前项目的功能有点击发声、切换图片、数据记录。其实可以基于此完成另一个小项目,比如电子宠物饲养类型的应用,可以喂食,抚摸发出叫声,切换宠物等操作。这和点击敲木鱼是殊途同归的,有时间和兴趣的可以自己尝试一下。