Flutter数据处理与展示插件processing_tree的使用

发布于 1周前 作者 songsunli 来自 Flutter

Flutter数据处理与展示插件processing_tree的使用

Processing Tree

Pub Package GitHub Issues GitHub Forks GitHub Stars GitHub License

Dart库用于在运行时构建和执行静态树形结构。

何时使用它

使用此库的主要想法是在运行时需要构建一个builder。例如,有一组输入文件将构建一些对象,但解析操作复杂且繁重。因此,意图是一次读取和解析,然后根据需要使用创建的builder。

开始使用

安装

遵循dart packages上的安装说明。

导入库

import 'package:processing_tree/processing_tree.dart';

使用

使用提供的一个builder准备树结构(请参阅示例),并与将在使用树时执行的代码进行绑定。最后调用build函数以获取TreeProcessor实例。从这一刻起,树已准备好处理数据,调用process函数以执行操作。

构建

目前只有可能通过使用提供的一个builder或编写自己的builder来构建树,该builder可以使用现有的builder作为基础。

TreeBuilder

最简单和最灵活的builder是TreeBuilder,它允许直接添加节点到其父节点。第一次调用addNode并将父节点设置为null用于定义根节点。每次调用addNode都需要传递新添加的节点的父节点。通过这种方式,可以通过builder API完成整个树结构。最后单次调用build返回TreeProcessor实例,可以使用该实例执行树。

StackedTreeBuilder

更高级版本的TreeBuilderStackedTreeBuilder,它允许以更有组织的方式工作。这减少了构建树时存储中间父引用的需求。在开始时,需要将构建树所需的数据传递给StackedTreeBuilder构造函数。之后,您可以添加或导航到构建的树结构。通过调用push方法,新节点附加到树上,并成为进一步操作的父节点。如果添加的节点不应成为下一个操作的父节点,则调用addChild,这在目标树中创建一个新的叶节点。要通过当前级别导航到子节点,请调用prevSiblingnextSibling。当给定级别的所有子节点都添加后,调用levelUp将选择当前父节点的父节点作为下一个操作的节点。最后单次调用build返回TreeProcessor实例,可以使用该实例执行树。

XmlTreeBuilder

这是最复杂的builder,它结合了XML反序列化和树构建。它可以将任何自定义XML解析为处理树。为了实现这一点,需要一个能够解释节点含义并选择适当委托和数据的数据提供者。为此实现了两种方法。对于简单的树,使用DelegateProvider;对于更高的控制级别,使用BuildCoordinator

使用XmlTreeBuilderDelegateProvider

使用DelegateProvider实现的数据解析设计用于那些可以根据XML元素名称轻松确定选择委托的XML。覆盖返回用于找到的XML元素的委托的方法delegate。需要覆盖的第二个方法是delegateData。它接受两个参数,delegateName与前一个方法相同,以及Map<String, dynamic>。此方法允许将从XML元素属性读取的数据处理成预期形式。结果可以是任何对象,在执行处理树时将传递给委托。下面是一个例子:

<add>
  <int value="12"/>
  <double value="42"/>
</add>

解析此XML时,将调用3次delegate方法,传递的delegateName将是addintdoubledelegate方法的责任是返回知道如何处理数据的正确委托。但是,数据是什么?

对于此XML,将调用3次delegateData方法,传递以下值:

  • delegateName: “add”, data: {}
  • delegateName: “int”, data: {“value”: “12”}
  • delegateName: “double”, data: {“value”: “42”}

现在我们理解最终的处理树会是什么样子?它看起来像这样:

             PNDelegate for add + data
             /                        \
  PNDelegate for int + data     PNDelegate for double + data

data中会有什么?简而言之,它是delegateData方法的结果。所以我们可以:

dynamic delegateData(String delegateName, Map<String, dynamic> rawData) {
   //...
   if (delegateName == "int") {
      return rawData;
   }
   //...
}

当然,如果您的委托看起来像这样,这将有效:

Action _intDelegate(dynamic context, dynamic data) {
  int value = int.tryParse(data["value"]);
  //.... do something with it
  return Action.proceed;
}

在这种情况下,与int节点关联的数据是Map<String, dynamic>,因此每次执行处理树时都会进行Stringint的转换。可以做得更好吗?考虑:

dynamic delegateData(String delegateName, Map<String, dynamic> rawData) {
   //...
   if (delegateName == "int") {
      return int.tryParse(data["value"]);
   }
   //...
}

现在我们的委托可以看起来像这样:

Action _intDelegate(dynamic context, dynamic data) {
  int value = data;
  //.... do something with it
  return Action.proceed;
}

还有一件事……每次调用delegateData都会有一个自己的映射,因此返回映射是完全可以的,如果需要收集更多来自节点的数据。但是,您可能希望在映射中预处理这些数据。考虑:

<SomeXmlNode int="22" string="Some text" bool="true"/>

以及一些初始预处理的代码:

dynamic delegateData(String delegateName, Map<String, dynamic> rawData) {
   //...
   if (delegateName == "int") {
      rawData["int"] = int.tryParse(data["int"]);
      rawData["bool"] = "true" == data["bool"];
      return rawData
   }
   //...
}

这只是开始……更多魔法请继续阅读。

使用XmlTreeBuilderBuildCoordinator

有两点原因你正在阅读这个。好奇心或者DelegateProvider不够用了……好吧,让我们见见他的更强兄弟BuildCoordinator。所有关于提供者的描述都是真实的,但有一点不同并且细节更多。代替两个方法delegatedelegateData,现在只有一个叫做requestData。它具有以下职责:

  • 返回要使用的委托(与DelegateProvider相同)
  • 返回委托的数据包(与DelegateProvider相同)
  • 返回节点类型(稍后覆盖)
  • 可选地返回您想要的任何对象。(稍后覆盖)

所有这些信息应该在一个名为ParsedItem的对象中返回。决策应基于存储在State类型输入参数中的更详细信息。State中包含的信息有:

  • parentNodeName - 当前正在处理的xml元素的名称
  • delegateName - 当前正在处理的xml元素的名称
  • constValDepth - 当前处理的xml元素在常量值分支中的深度(稍后覆盖)
  • isLeaf - 如果为真,则此xml元素没有子元素
  • data - 从xml元素属性收集的数据(与DelegateProvider相同)

返回委托及其数据的想法与DelegateProvider完全相同。

节点类型是什么?

在上一节中有一些关于节点返回类型的讨论。有关更多细节,请参阅ParsedItemType文档。引入它的原因是显而易见的。事实证明,算法的XML表示包含一些只是结构化数据的节点,这些节点需要解析,但不需要任何委托来执行/处理。因此,简而言之,某些XML节点可以在解析过程中被“消耗”,而不是成为处理树的一部分。快速示例:

<ShowText>
  <color value="red"/>
  <text value="text to show"/>
</ShowText>

看着这个,你会觉得只有元素ShowText执行任何操作,其他元素只是需要一次处理的常量数据。这就是为什么引入了BuildCoordinator的原因。因此,如果textcolor值应在解析过程中被消耗,则返回ParsedItemType.constValue作为项目类型,否则如果元素应添加到树中,则返回ParsedItemType.owner。就是这样……几乎……等等,对于constValue类型的节点,我应该返回委托吗?嗯……是的,但:此委托将在解析过程中立即调用!为什么?让我们看看,假设构建树的Widget

<ListView>
  <Text data="text to show"/>
</ListView>

假设ListViewText都是Widget。如果Text被标记为constValue,那么我们可以处理数据,但我们没有Widget实例……构建器不知道每个xml节点真正意味着什么。这就是委托的作用,它知道如何处理数据。执行路径将是这样的:

  1. 解析xml,找到ListView,调用协调器上的requestData
  2. 协调器返回可以构建ListView的委托+信息,这是一个ParsedItemType.owner
  3. 解析xml,找到Text,调用协调器上的requestData
  4. 协调器返回可以构建Text的委托+信息,这是一个ParsedItemType.constValue
  5. 解析器看到这是一个ParsedItemType.constValue,所以立即调用协调器返回的委托。
  6. 委托从Text元素中获取数据,构建小部件并返回它。
  7. 解析器从委托中获得结果,将其添加到ListView的数据集合中。

这看起来模糊,看起来过于复杂,而且缺乏许多细节……你需要实验才能理解。好消息是:就这样!开玩笑的。这里还有更多:继续阅读。

解密:ParsedItem中的extObj是什么?

如果你错过了这个解释,那就在这里。有时需要在解析当前元素的所有子元素之后执行一些额外的操作。这时出现了BuildCoordinator的第二次覆盖方法step。此方法在解析xml元素时会被多次调用。当发现处理子元素开始和结束时,以及解析完成后,每次调用时都会收到一个ParsedItemType实例,但这可能还不够。有时需要将一些额外的数据与此解析节点关联起来。这就是requestData返回的extObj。这是任何你喜欢的东西,对构建器是透明的,不会被缓存任何地方。它只在解析过程中被ParsedItemType引用,后来所有的ParsedItemType实例都被丢弃。

解密:ParsedItem中的constValDepth是什么?

这很简单:) 任何常量值元素都可以有许多子元素(每个必须是常量值)。如果需要知道当前解析的常量值类型的元素以及解析器在这个分支中读取值的深度,可以使用constValDepth

处理树的执行 (TreeProcessor)

每个builder最终都会返回一个TreeProcessor实例,可以使用它来执行树。有两种类型的执行:normalinverted。由于这些方法显著不同,更多关于执行模型的信息在下一节。

正常执行

默认情况下,每个builder返回一个准备执行正常操作的TreeProcessor实例。运行树是通过调用名为process的方法完成的,它接受一个任何类型的参数。此方法将此参数作为第一个参数传递给处理树中的每个委托。然后根据返回的Action值决定下一步做什么。请参阅Action类型的文档以了解每个值会发生什么。一般来说,正常执行被认为是从根到每个叶节点访问每个节点及其委托(先序遍历)。这种执行允许使用所有Action枚举值。因此,它允许标记树中的位置并在稍后再次处理某些分支,或者允许重复执行当前节点。

倒序执行

有时需要从下往上处理树,收集较低层的数据并进行处理。为此,请调用TreeProcessor上的inverted方法,作为结果会返回另一个TreeProcessor实例。此新实例将在调用process后执行倒序树执行。有什么区别?在倒序执行中,每个节点仍然会被访问,但开始于叶子。保证对于有多个子节点的每个节点,其委托将在所有子节点处理后被调用。访问子节点的顺序与普通节点相同,但委托是在子节点被访问后调用的。

我正在执行……我的结果在哪里?

这很复杂。总的来说,可能没有结果,一切都取决于你的委托实际做了什么。但是有一个特殊的地方可以搜索结果。在类TreeProcessor的方法process中接受一个任何类型的参数。这个对象在执行树时传递给所有委托,它被称为PNDelegate签名中的上下文。因此:

TreeProcessor processor = builder.build(...);
ResultCollector result = ResultCollector();
processor.process(result);
//...

Action myDelegate(dynamic context, dynamic data) {
  ResultCollector result = context; //在这里你可以得到它!
  //... 执行某些操作
  result.addData(...);  //调用,存储,等等
}

更多关于Flutter数据处理与展示插件processing_tree的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter数据处理与展示插件processing_tree的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,processing_tree 是一个用于 Flutter 的数据处理与展示插件,可以帮助开发者以树状结构来处理和展示数据。尽管 processing_tree 可能不是一个广泛认知的 Flutter 插件(由于 Flutter 社区插件众多,且更新频繁,具体插件的流行度和可用性可能随时间变化),但基于你的要求,我将提供一个假设性的代码案例,展示如何在 Flutter 中使用类似的树状结构数据处理与展示功能。

请注意,以下代码是一个示例,假设 processing_tree 插件提供了基本的树节点创建、数据绑定和展示功能。如果 processing_tree 插件的实际 API 与此不同,请查阅其官方文档进行调整。

示例代码

1. 添加依赖

首先,在 pubspec.yaml 文件中添加 processing_tree 插件的依赖(如果它存在的话,实际插件名可能不同):

dependencies:
  flutter:
    sdk: flutter
  processing_tree: ^x.y.z  # 替换为实际版本号

然后运行 flutter pub get

2. 定义树节点数据模型

class TreeNode {
  String title;
  List<TreeNode> children;

  TreeNode({required this.title, this.children = const []});
}

3. 创建树数据

List<TreeNode> createTreeData() {
  return [
    TreeNode(
      title: 'Root',
      children: [
        TreeNode(
          title: 'Child 1',
          children: [
            TreeNode(title: 'Grandchild 1.1'),
            TreeNode(title: 'Grandchild 1.2'),
          ],
        ),
        TreeNode(
          title: 'Child 2',
          children: [
            TreeNode(title: 'Grandchild 2.1'),
          ],
        ),
      ],
    ),
  ];
}

4. 使用 ProcessingTree 插件展示树

假设 processing_tree 插件提供了一个 ProcessingTree 小部件来展示树状结构:

import 'package:flutter/material.dart';
import 'package:processing_tree/processing_tree.dart'; // 假设插件提供了这个导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Tree Data Processing',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: TreeDataScreen(),
    );
  }
}

class TreeDataScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    List<TreeNode> treeData = createTreeData();

    return Scaffold(
      appBar: AppBar(
        title: Text('Tree Data Processing'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: ProcessingTree(
          data: treeData,
          itemBuilder: (context, node) {
            return ListTile(
              title: Text(node.title),
              trailing: Icon(
                node.children.isNotEmpty ? Icons.expand_more : Icons.chevron_right,
              ),
            );
          },
        ),
      ),
    );
  }
}

注意

  • 上述代码中的 ProcessingTree 小部件及其 dataitemBuilder 属性是假设性的。实际使用时,请根据 processing_tree 插件的文档调整代码。
  • 如果 processing_tree 插件不存在或功能不符,可以考虑使用其他树状结构展示插件,如 flutter_tree 或手动实现树状结构展示逻辑。
  • 确保查阅最新的 Flutter 社区和插件市场,以获取最适合当前 Flutter 版本的插件。

希望这个示例能帮助你理解如何在 Flutter 中处理和展示树状结构数据。如果有具体的 processing_tree 插件文档或示例,请参考其官方资源进行调整。

回到顶部