Flutter 组件集录 | 组件案例是怎么展示的?

494 阅读5分钟

本文将从 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 站 。