本文将从 FlutterUnit 如何展示一个组件信息为线索,展开介绍 FlutterUnit 对组件管理的处理方式。
- | - |
---|---|
1. 案例组件模块
FlutterUnit 按照需求划分功能模块,其中 组件集录 展示的内容盛放在 widget_system/widgets
模块下。按照不同的类别,盛放在对应的文件夹下:
拿 Text 组件为例,其中盛放的 desc_zh-CN.json
是该组件展示信息的中文描述数据;其余的 node 文件表示 Text 组件需要展示的节点内容,这些文件都是可以 独立展示 的组件,这也是 FlutterUnit 中的组件示例直接复制就可以用的原因。
比如文字应用的案例,通过 TextDemo2 组件展示:
class TextDemo2 extends StatelessWidget {
const TextDemo2({super.key});
@override
Widget build(BuildContext context) {
const TextStyle style = TextStyle(
fontSize: 50,
color: Colors.white,
backgroundColor: Colors.black,
shadows: [
Shadow(color: Colors.cyanAccent, offset: Offset(1, 1), blurRadius: 10),
Shadow(color: Colors.blue, offset: Offset(-0.1, 0.1), blurRadius: 10),
]);
return const Text("张风捷特烈", style: style);
}
}
2. 从数据库数据到界面展示
上一篇说过,FlutterUnitTool 会将每个组件中的 desc_zh-CN.json
解析并放入数据库。组件信息记录在 widget 表中:
一个组件对应若干个展示节点,每个节点信息记录在 node
表中,通过 widget_id
和组件表关联:
这样查询组件对应的展示节点,根据 widget_id 列表查询即可。应用程序中组件数据相关的处理放在 widget_repository ,表示数据仓储层。
其中 database 文件夹维护数据库的增删改查,比如负责查询节点列表的类是 NodeDbRepository
,它依赖 NodeDao
操作数据库,得到 data 数据后,映射为 NodeModel
列表,以供视图层使用:
NodeDao 中通过 sql 语句从数据库中查询节点信息:
---->[widget_repository/lib/src/database/dao/node_dao.dart]----
//根据 id 查询组件 node
Future<List<Map<String, dynamic>>> queryById(int id) async {
return await database.rawQuery(
"SELECT name,subtitle,code,priority "
"FROM node "
"WHERE widgetId = ? ORDER BY priority",
[id]);
}
FlutterUnit 的 组件集录
功能实现,主要基于 bloc 进行状态管理。组件的展示信息由 widget_detail_bloc 维护,点击组件条目时触发 queryDetail
通过仓储查询数据,产出 DetailWithData
状态。其中记录着详情页中需要的数据:
class DetailWithData extends DetailState {
final WidgetModel widgetModel;
final List<WidgetModel> links;
final List<NodeModel> nodes;
const DetailWithData({
required this.widgetModel,
required this.nodes,
required this.links,
});
视图层的每一个案例节点, 通过 NodeDisplay
组件展示,其中入参 NodeWidgetMapper
用于将组件节点,映射为展示的组件,这一点也是 FlutterUnit 组件集录得以实现的关键点。
3. 处理案例组件映射关系
在之前的版本中,我通过一个 WidgetsMap#map
静态方法,根据组件名称返回对应的案例节点组件列表,以此实现组件的映射关系。这种方式虽然有效,但是维护起来比较繁琐,新增案例时需要找到对应的组件名,加入相关案例:
在新版本中,我实现了一种更为简单、有效、易于管理的方式。通过 代码自动生成 来创建组件和展示节点间的映射关系。每个案例可以通过 组件id + 第几个
唯一确定,可以创建下的映射函数,
desc_zh-CN.json
中已经存在 组件id, 节点对应的序列可以通过解析时的索引得到。最后对于 mapNodeDisplay
来说,就剩下一个问题:
如何获取对应案例的组件名?
组件名虽然可以在 desc_zh-CN.json
中手动给出,但这么做多了一道工序,而且如果更新组件名,还需要同步修改。其实一个文件中的第一个组件叫什么名字,可以通过正则表达式来匹配:
对应的 dart 测试代码如下所示:
void main() async{
String code = '...';
RegExp repClassName = RegExp(r'(?<=class) (\w+) extends .*(Widget \{)',multiLine: true);
Match? match = repClassName.firstMatch(code);
print(match?.group(1));
}
此时在 FlutterUnitTool 的数据分析中,可以创建一个 Node 节点的数据展示。映射关系中的键与值就准备就绪,最后只需要一点点小魔法,遍历当前的节点数据,生成 mapNodeDisplay
对应的源代码即可:
处理代码如下所示,这样 FlutterUnitTool 就可以在 FlutterUnit 组件集录数据方面,从解析数据入库,到自动生成映射文件,达到一站式辅助的功能。让组件案例节点的维护变得非常轻松 ~
---->[FlutterUnitTool]----
void createNodeMap() async {
String savePath = '...';
String content = '';
for(NodePo po in state.nodes){
content +=" '${po.widgetId}#${po.priority}' => const ${po.className}(),\n";
}
String template = '''import 'package:flutter/cupertino.dart';
import 'exp/other_unit.dart';
import 'exp/proxy_unit.dart';
import 'exp/render_object_unit.dart';
import 'exp/sliver_unit.dart';
import 'exp/stateful_unit.dart';
import 'exp/stateless_unit.dart';
Widget mapNodeDisplay(int widgetId, int nodePriority) {
String name = '\$widgetId#\$nodePriority';
return switch (name) {
$content
_ => const SizedBox(),
};
}''';
File(savePath).writeAsString(template);
}
4. 案例展示组件 NodeDisplay
新版的 FlutterUnit 对案例展示的布局效果进行了优化,如下是一个案例节点,它包括 标题
、案例组件
、操作工具
、 介绍信息
四个部分。
其中点击代码按钮,可以展开对应代码。新版的代码展示在女友的帮助下,区支持了局部选择复制; 另外可收叠的面板区域,封装为 CodeDisplay 组件。使用 tolyui 中的 TolyCollapse 组件实现收叠的功能:
这样就从描述信息到数据库,再到解析映射组件,介绍了 FlutterUnit 中一个组件的案例是如何展示的。那本文就到这里,后期还会盘点一些 FlutterUnit 实现过程中的一些有趣的知识。敬请期待~
更多文章和视频知识资讯,大家可以关注我的公众号、掘金和 B 站 。