Flutter可视化Widget树插件graph_your_widget_tree的使用

Flutter可视化Widget树插件graph_your_widget_tree的使用

graph_your_widget_tree 提供了一个名为 Graph 的小部件,该小部件可以渲染由 WidgetEntry 及其子节点表示的控件树模型。

这个包主要用于与制作幻灯片的其他包一起使用,例如 flutter_deckslick_slides,而不是用于生产应用程序。

功能

此包提供了以下功能:

  • ✅ 渲染控件树的图形
  • ✅ 为每个控件节点设置样式
  • ✅ 检测来自控件树的事件
  • ✅ 详细通知布局计算的结果

所有这些功能都是为了让您的幻灯片更具交互性。

开始使用

作为一个快速入门示例,您可以将 Graph 小部件放置在代码如下所示的位置:

Graph(
  root: WidgetEntry.single(
    name: 'MaterialApp',
    child: WidgetEntry.leaf(name: 'Scaffold'),
  ),
),

这段代码将创建一个根节点为 MaterialApp,其子节点为 Scaffold 的控件树,绘制出来的效果如下图所示:

一个简单的控件树

使用方法

基础用法

要绘制控件树的图形,只需将 Graph 小部件放置在需要绘制的地方即可。

Graph(),

Graph 需要一个 WidgetEntry 对象作为控件树的根节点。

Graph(
  root: WidgetEntry.single(),
)

WidgetEntry 表示控件树的一个节点。由于 Flutter 控件有三种模式:

  • 有一个子节点,如 SizedBoxCenter
  • 有多个子节点,如 ColumnStack
  • 没有子节点,即所谓的 leaf,如 ImageRichText

因此,WidgetEntry 有三种构造函数,分别是 WidgetEntry.single()WidgetEntry.multiple()WidgetEntry.leaf()

您只需要使用 WidgetEntry 描述控件树的结构,并将其根节点传递给 Graph 即可。

例如,如果运行以下代码:

Graph(
  root: WidgetEntry.single(
    name: 'MaterialApp',
    child: WidgetEntry.multiple(
      name: 'Scaffold',
      children: [
        WidgetEntry.multiple(
          name: 'AppBar', 
          children: [
            WidgetEntry.single(
              name: 'IconButton',
              child: WidgetEntry.leaf(name: 'Icon'),
            ),
          ],
        ),
        WidgetEntry.multiple(
          name: 'Column', children: [
            WidgetEntry.leaf(name: 'Text'),
            WidgetEntry.leaf(name: 'Text'),
          ],
        ),
        WidgetEntry.leaf(
          name: 'Floating\nActionButton',
        ),
      ],
    ),
  ),
),

这将绘制出以下控件树:

一个样本控件树

样式

每个 WidgetEntry 可以通过 GraphTheme 进行样式设置。

由于 GraphTheme 是一个 InheritedWidget,只要它位于 Graph 的祖先中,就可以将其放置在任何位置。

GraphTheme(
  child: Graph(),
),

GraphTheme 有两个选项用于样式设置,defaultThemeextraThemes

defaultTheme 会应用到所有的 WidgetEntry 作为默认样式。

例如,您可以使用以下代码使所有文本和方框的边框加粗:

GraphTheme(
  child: Graph(),
  defaultTheme: const GraphThemeData(
    textStyle: TextStyle(fontWeight: FontWeight.w800),
    borderWidth: 4,
  ),
),

加粗的文本和方框

extraThemes 用于单独设置特定 WidgetEntry 的样式。

由于 extraThemes 的类型是 Map<Enum, GraphThemeData>,您可以定义任何 enum 类型作为 extraThemes 的键,并为其设置关联的 GraphThemeData 作为值。

enum WidgetAppearance { normal, focused, disabled }

GraphTheme(
  defaultTheme: const GraphThemeData(),
  extraThemes: const {
    WidgetAppearance.focused: GraphThemeData(
      textStyle: TextStyle(color: Colors.blue),
      borderWidth: 4,
      borderColor: Colors.blue,
    ),  
    WidgetAppearance.disabled: GraphThemeData(
      textStyle: TextStyle(color: Colors.grey),
      borderWidth: 1,
      borderColor: Colors.grey,
    ),  
  },
  child: Graph(),
),

然后,如果您想将其中一个 extraThemes 应用到 WidgetEntry,可以通过传递 extraThemes 的键之一(在这种情况下是 WidgetAppearance.focusedWidgetAppearance.disabled)来设置 type

当您将 WidgetAppearance.focused 设置为 IconButtonWidgetEntry,并将 WidgetAppearance.disabled 设置为 FloatingActionButton 时,

Graph(
  root: WidgetEntry.single(
    name: 'MaterialApp',
    child: WidgetEntry.multiple(
      name: 'Scaffold',
      children: [
        WidgetEntry.multiple(
          name: 'AppBar',
          children: [
            WidgetEntry.single(
              name: 'IconButton',
              child: WidgetEntry.leaf(name: 'Icon'),
              type: WidgetAppearance.focused,
            ),
          ],
        ),
        WidgetEntry.multiple(
          name: 'Column',
          children: [
            WidgetEntry.leaf(name: 'Text'),
            WidgetEntry.leaf(name: 'Text'),
          ],
        ),
        WidgetEntry.leaf(
          name: 'Floating\nActionButton',
          type: WidgetAppearance.disabled,
        ),
      ],
    ),
  ),
),

Graph 将绘制以下控件树:

带有样式的方框

重建和更新UI

由于所有布局和样式会在每次重建时更新,您可以使用任何方法管理“每个控件应该如何显示”的状态,例如 StatefulWidgetriverpod 或者 Get

这使得您可以制作具有动态动画控件树的幻灯片。

事件

Graph 目前有两个回调,onHoveronTap。当每个事件发生在 WidgetEntry 上时,这两个回调会被调用。

Graph(
  onHover: (entry) {
    log('hovered: ${entry?.name}');
  },
  onTap: (entry) {
    log('tapped: ${entry.name}');
  },
)

通知

通过在 Graph 上方放置 NotificationListener<RenderDetailNotification>,您可以监听渲染控件树的每次更新。

NotificationListener<RenderDetailNotification> {
  onNotification: (notification) {
    // 对渲染细节进行处理。
    return false;
  },
  child: SomeWidget(
    child: Graph(),
  ),
}

由于 notification 包含了 Graph 上所有渲染控件的详细信息,它可以允许您基于控件的位置叠加额外的小部件。

有关 NotificationListener 的更多信息,请参阅 Flutter 文档

完整示例

以下是完整的示例代码:

import 'dart:developer';

import 'package:flutter/material.dart';
import 'package:graph_your_widget_tree/graph_your_widget_tree.dart';

void main() {
  runApp(const MainApp());
}

enum BoxType {
  none,
  focus,
  highlighted,
}

enum WidgetAppearance { normal, focused, disabled }

class MainApp extends StatelessWidget {
  const MainApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: NotificationListener<RenderDetailNotification>(
          onNotification: (notification) {
            // 对渲染细节进行处理。
            return false;
          },
          child: Center(
            child: GraphTheme(
              defaultTheme: const GraphThemeData(),
              extraThemes: const {
                WidgetAppearance.focused: GraphThemeData(
                  textStyle: TextStyle(color: Colors.blue),
                  borderWidth: 4,
                  borderColor: Colors.blue,
                ),
                WidgetAppearance.disabled: GraphThemeData(
                  textStyle: TextStyle(color: Colors.grey),
                  borderWidth: 1,
                  borderColor: Colors.grey,
                ),
              },
              child: Padding(
                padding: const EdgeInsets.all(32),
                child: Graph(
                  root: WidgetEntry.single(
                    name: 'MaterialApp',
                    child: WidgetEntry.multiple(
                      name: 'Scaffold',
                      children: [
                        WidgetEntry.multiple(
                          name: 'AppBar',
                          children: [
                            WidgetEntry.single(
                              name: 'IconButton',
                              child: WidgetEntry.leaf(name: 'Icon'),
                              type: WidgetAppearance.focused,
                            ),
                          ],
                        ),
                        WidgetEntry.multiple(
                          name: 'Column',
                          children: [
                            WidgetEntry.leaf(name: 'Text'),
                            WidgetEntry.leaf(name: 'Text'),
                          ],
                        ),
                        WidgetEntry.leaf(
                          name: 'Floating\nActionButton',
                          type: WidgetAppearance.disabled,
                        ),
                      ],
                    ),
                  ),
                  onHover: (entry) {
                    log('hovered: ${entry?.name}');
                  },
                  onTap: (entry) {
                    log('tapped: ${entry.name}');
                  },
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

更多关于Flutter可视化Widget树插件graph_your_widget_tree的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter可视化Widget树插件graph_your_widget_tree的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于Flutter的可视化Widget树插件graph_your_widget_tree,这里是一个如何使用它的基本代码案例。这个插件允许开发者在开发过程中可视化Flutter应用中的Widget树,有助于调试和理解应用的布局结构。

首先,确保你已经在pubspec.yaml文件中添加了graph_your_widget_tree依赖:

dependencies:
  flutter:
    sdk: flutter
  graph_your_widget_tree: ^最新版本号  # 请替换为当前最新版本号

然后运行flutter pub get来获取依赖。

接下来,你可以在你的Flutter应用中使用该插件。以下是一个简单的示例,展示如何在应用中集成并使用graph_your_widget_tree

import 'package:flutter/material.dart';
import 'package:graph_your_widget_tree/graph_your_widget_tree.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Graph Your Widget Tree Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              ElevatedButton(
                onPressed: () {
                  // 显示Widget树
                  showDialog(
                    context: context,
                    builder: (BuildContext context) {
                      return AlertDialog(
                        title: Text('Widget Tree Graph'),
                        content: SingleChildScrollView(
                          child: Container(
                            height: 300, // 可以根据需要调整高度
                            child: WidgetTreeGraph(
                              // 这里传入根Widget,通常是Scaffold或者MaterialApp的子Widget
                              widget: context.widget.body, // 注意:这里直接传入context.widget.body可能不适用于所有情况,
                                                          // 实际使用时,你可能需要传入当前Scaffold或特定Widget的引用。
                              // 对于更复杂的应用,可能需要通过某种方式获取到要可视化的Widget的引用。
                            ),
                          ),
                        ),
                        actions: <Widget>[
                          ElevatedButton(
                            onPressed: () {
                              Navigator.of(context).pop();
                            },
                            child: Text('Close'),
                          ),
                        ],
                      );
                    },
                  );
                },
                child: Text('Show Widget Tree'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

注意

  1. 在上面的代码中,context.widget.body直接用于获取要可视化的Widget,这在实际应用中可能并不适用,因为context.widget通常指的是当前Widget本身,而不是其子Widget。在实际应用中,你可能需要通过某种方式(如全局键或者特定的Widget引用)来获取你想要可视化的Widget。

  2. WidgetTreeGraph组件可能需要根据实际情况调整其参数和布局,以适应你的应用需求。

  3. 由于graph_your_widget_tree插件的具体API可能会随着版本更新而变化,因此请参考该插件的最新文档和示例代码以获取最准确的信息。

  4. 如果插件没有直接提供WidgetTreeGraph这样的组件(因为API可能会变化),你可能需要查看插件的示例代码或者文档来了解如何正确集成和使用它。

这个示例展示了如何在Flutter应用中集成并使用graph_your_widget_tree插件来可视化Widget树。然而,由于插件的具体实现和API可能会变化,因此请务必参考插件的最新文档和示例代码。

回到顶部