Flutter组织架构图插件org_chart的使用

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

Flutter组织架构图插件 org_chart 的使用

org_chart 是一个功能丰富的Flutter插件,用于创建可拖拽、缩放和平移的组织架构图。它支持多种自定义选项,如节点形状、箭头样式等,并且适用于所有Flutter支持的平台。

重要提示

这是我的第一个包,如果你有任何想法或建议,请不要犹豫联系我。欢迎提交问题或拉取请求。

待办事项

  • [✅] 实现相对稳定且易于定制的API
  • [✅] 添加方向支持
  • [✅] 添加箭头绘制自定义
  • ❌ 添加箭头样式
  • ❌ 添加箭头动画
  • ❌ 编写详细文档

如果你有更多需要添加的内容,请提交问题或拉取请求!

组织架构图功能

  • 拖拽和放置
  • 缩放和平移
  • 搜索
  • 折叠/展开节点
  • 极易定制的节点形状

警告

如果在 onDrop 函数中 isTargetSubnodetrue,则将目标设置为拖动节点的父节点会导致应用崩溃!现在这个检查会在后台自动完成。

使用方法

要使用此包,请在 pubspec.yaml 文件中添加 org_chart 作为依赖项。

dependencies:
  org_chart: ^latest_version

别忘了导入包:

import 'package:org_chart/org_chart.dart';

控制器实现

final OrgChartController<Map> orgChartController = OrgChartController<Map>(
  boxSize: const Size(200, 100),
  items: [
    {"title": 'CEO', "id": '1', "to": null},
    {"title": 'HR Manager: John', "id": '2', "to": '1'},
    {"title": 'HR Officer: Jane', "id": '3', "to": '2'},
    {"title": 'Project Manager: Zuher', "id": '4', "to": '1'},
  ],
  idProvider: (data) => data["id"],
  toProvider: (data) => data["to"],
  toSetter: (data, newID) => data["to"] = newID,
);

小部件实现

OrgChart(
  controller: orgChartController,
  curve: Curves.elasticOut, // 自定义动画曲线
  duration: 500, // 自定义动画持续时间
  isDraggable: true, // 启用或禁用拖拽
  builder: (details) {
    return Card(
      color: details.beingDragged
          ? Colors.blue
          : details.isOverlapped
              ? Colors.green
              : null,
      elevation: 10,
      child: Center(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(details.item["title"]),
            ElevatedButton(
              onPressed: () {
                details.hideNodes(!details.nodesHidden);
              },
              child: Text(details.nodesHidden ? 'Press to Unhide' : 'Press to Hide'),
            ),
          ],
        ),
      ),
    );
  },
  optionsBuilder: (item) {
    return [
      const PopupMenuItem(value: 'Remove', child: Text('Remove')),
    ];
  },
  onOptionSelect: (item, value) {
    if (value == 'Remove') {
      orgChartController.removeItem(item["id"], ActionOnNodeRemoval.unlink);
    }
  },
  onDrop: (dragged, target, isTargetSubnode) {
    if (isTargetSubnode) {
      showDialog(
        context: context,
        builder: (_) {
          return AlertDialog(
            title: const Text('Error'),
            content: const Text('You cannot drop a node on a subnode'),
            actions: [
              TextButton(
                onPressed: () {
                  Navigator.of(context).pop();
                },
                child: const Text('OK'),
              ),
            ],
          );
        },
      );
      orgChartController.calculatePosition();
      return;
    }
    dragged["to"] = target["id"];
    orgChartController.calculatePosition();
  },
)

示例代码

以下是一个完整的示例代码,展示了如何使用 org_chart 插件来创建一个简单的组织架构图:

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

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

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

  [@override](/user/override)
  State<MainApp> createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return const MaterialApp(home: Example2());
  }
}

class Example2 extends StatefulWidget {
  const Example2({super.key});

  [@override](/user/override)
  State<Example2> createState() => _Example2State();
}

class _Example2State extends State<Example2> {
  final OrgChartController<Map> orgChartController = OrgChartController<Map>(
    boxSize: const Size(150, 80),
    items: [
      {"id": '0', "text": 'Main Block'},
      {"id": '1', "text": 'Block 2', "to": '0'},
      {"id": '2', "text": 'Block 3', "to": '0'},
      {"id": '3', "text": 'Block 4', "to": '1'},
    ],
    idProvider: (data) => data["id"],
    toProvider: (data) => data["to"],
    toSetter: (data, newID) => data["to"] = newID,
  );

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Stack(
      children: [
        Container(
          decoration: BoxDecoration(
            gradient: LinearGradient(
              colors: [Colors.blue.shade100, Colors.blue.shade200],
              begin: Alignment.bottomLeft,
              end: Alignment.topRight,
            ),
          ),
          child: Scaffold(
            backgroundColor: Colors.transparent,
            body: Stack(
              children: [
                Center(
                  child: OrgChart<Map>(
                    cornerRadius: 10,
                    controller: orgChartController,
                    isDraggable: true,
                    linePaint: Paint()
                      ..color = Colors.black
                      ..strokeWidth = 5
                      ..style = PaintingStyle.stroke,
                    onTap: (item) {
                      orgChartController.addItem({
                        "id": orgChartController.uniqueNodeId,
                        "text": 'New Block',
                        "to": item["id"],
                      });
                      setState(() {});
                    },
                    onDoubleTap: (item) async {
                      String? text = await getBlockText(context, item);
                      if (text != null) setState(() => item["text"] = text);
                    },
                    builder: (details) {
                      return GestureDetector(
                        child: Card(
                          elevation: 5,
                          color: details.isBeingDragged
                              ? Colors.green.shade100
                              : details.isOverlapped
                                  ? Colors.red.shade200
                                  : Colors.teal.shade50,
                          child: Center(
                            child: Text(
                              details.item["text"],
                              style: TextStyle(color: Colors.purple.shade900, fontSize: 20),
                            ),
                          ),
                        ),
                      );
                    },
                    optionsBuilder: (item) {
                      return [
                        const PopupMenuItem(value: 'Remove', child: Text('Remove')),
                      ];
                    },
                    onOptionSelect: (item, value) {
                      if (value == 'Remove') {
                        orgChartController.removeItem(item["id"], ActionOnNodeRemoval.unlink);
                      }
                    },
                    onDrop: (dragged, target, isTargetSubnode) {
                      if (isTargetSubnode) {
                        showDialog(
                          context: context,
                          builder: (_) {
                            return AlertDialog(
                              title: const Text('Error'),
                              content: const Text('You cannot drop a node on a subnode'),
                              actions: [
                                TextButton(
                                  onPressed: () {
                                    Navigator.of(context).pop();
                                  },
                                  child: const Text('OK'),
                                ),
                              ],
                            );
                          },
                        );
                        orgChartController.calculatePosition();
                        return;
                      }
                      dragged["to"] = target["id"];
                      orgChartController.calculatePosition();
                    },
                  ),
                ),
                const Positioned(
                  bottom: 20,
                  left: 20,
                  child: Text(
                    'Tap to add a node, double tap to change text\n'
                    'drag and drop to change hierarchy\n'
                    'right click / tap and hold to remove \n'
                    'Drag in the empty space to pan the chart, zoom in and out.\n',
                  ),
                )
              ],
            ),
            floatingActionButton: FloatingActionButton.extended(
              label: const Text('Reset & Change Orientation'),
              onPressed: () {
                orgChartController.switchOrientation();
              },
            ),
          ),
        ),
      ],
    );
  }

  Future<String?> getBlockText(BuildContext context, Map<dynamic, dynamic> item) async {
    final String? text = await showDialog(
      context: context,
      builder: (context) {
        String text = item["text"];
        return AlertDialog(
          title: const Text('Enter Text'),
          content: TextFormField(
            initialValue: item["text"],
            onChanged: (value) {
              text = value;
            },
          ),
          actions: [
            TextButton(
              onPressed: () {
                Navigator.of(context).pop();
              },
              child: const Text('Cancel'),
            ),
            TextButton(
              onPressed: () {
                Navigator.of(context).pop(text);
              },
              child: const Text('OK'),
            ),
          ],
        );
      },
    );
    return text;
  }
}

更多关于Flutter组织架构图插件org_chart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter组织架构图插件org_chart的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是如何在Flutter中使用org_chart插件来创建组织架构图的示例代码。假设你已经安装了org_chart插件,可以通过以下步骤来实现。

1. 添加依赖

首先,确保在pubspec.yaml文件中添加了org_chart的依赖:

dependencies:
  flutter:
    sdk: flutter
  org_chart: ^最新版本号  # 请替换为最新版本号

然后运行flutter pub get来安装依赖。

2. 导入插件

在你的Flutter项目的Dart文件中导入org_chart插件:

import 'package:org_chart/org_chart.dart';

3. 创建数据模型

为了创建组织架构图,你需要一个数据模型来表示每个节点(员工)。这里是一个简单的示例:

class OrgNode {
  String name;
  List<OrgNode> children;

  OrgNode({required this.name, this.children = const []});
}

4. 构建组织架构图

在你的Flutter Widget中,使用OrgChart组件来构建组织架构图。以下是一个完整的示例:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Org Chart Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: OrgChartScreen(),
    );
  }
}

class OrgChartScreen extends StatefulWidget {
  @override
  _OrgChartScreenState createState() => _OrgChartScreenState();
}

class _OrgChartScreenState extends State<OrgChartScreen> {
  // 创建组织架构数据
  final OrgNode rootNode = OrgNode(
    name: 'CEO',
    children: [
      OrgNode(
        name: 'CTO',
        children: [
          OrgNode(name: 'Developer 1'),
          OrgNode(name: 'Developer 2'),
        ],
      ),
      OrgNode(
        name: 'CFO',
        children: [
          OrgNode(name: 'Accountant 1'),
          OrgNode(name: 'Accountant 2'),
        ],
      ),
      OrgNode(
        name: 'CHO',
        children: [
          OrgNode(name: 'HR 1'),
          OrgNode(name: 'HR 2'),
        ],
      ),
    ],
  );

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Org Chart Example'),
      ),
      body: OrgChart(
        data: rootNode,
        nodeBuilder: (context, node) {
          return OrgChartNode(
            title: Text(node.name),
            subTitle: node.children.isEmpty
                ? null
                : Text('${node.children.length} Direct Reports'),
          );
        },
      ),
    );
  }
}

5. 运行应用

确保你的开发环境已经配置好,然后运行应用:

flutter run

这个示例展示了如何使用org_chart插件来创建一个简单的组织架构图。你可以根据需要自定义节点样式、布局和其他功能。请查阅org_chart插件的官方文档以获取更多详细信息和高级用法。

回到顶部