【问题标题】:Flutter directed graph. Can I use CustomPainter Class with custom widgets?颤振有向图。我可以将 CustomPainter 类与自定义小部件一起使用吗?
【发布时间】:2020-04-30 05:52:43
【问题描述】:

我想用颤振构建一个如下图所示的有向图。 我不知道从哪里开始。我在互联网上搜索没有成功。这种图需要哪些算法? 我尝试使用自定义画家类构建此图。我不知道如何在自定义画家类中使用自定义小部件。 (例如,旁边有人物图片和文字的矩形)。 我只能画矩形和线条...... 缩放和平移我想我可以用 GestureDetector 类来做。 图表应该可以动态定制。

【问题讨论】:

  • 你对节点之间关系的逻辑是什么?
  • 您可以发布您使用自定义画家类编写的代码吗?您已经尝试过提到了......!还有节点应该如何放置在图中的逻辑。否则,这个问题似乎太宽泛了,看起来像是在要求一个有效的实现。在我看来,这绝对可以在 Flutter 中使用自定义画家来实现。

标签: flutter dart flutter-layout flutter-canvas


【解决方案1】:

CustomPainter 似乎有点矫枉过正。使用堆栈。您需要先计算每个节点或连接的位置和大小,然后使用 Positioned 小部件放置它们。

【讨论】:

    【解决方案2】:

    您需要拆分任务。

    1. 使图层缩放和移动整个场景,您可以使用 带有 onScale 事件 + Transform.scale 小部件的 GestureDetector 小部件, (检查 zoom_widget 包)。
    2. 使单个项目可拖动。使用 GestureDetector + onPan 事件。
    3. 使用 CustomPainter 在元素之间绘制连接线。我已经做了直线来展示主要逻辑。

    ..添加额外的逻辑如何添加新项目。

    更新: codepen interactive version 由 @maks 创建

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            body: Center(
              child: Container(
                alignment: Alignment.center,
                child: ItemsScene(),
                decoration: BoxDecoration(
                  border: Border.all(
                    color: Colors.blueAccent,
                  ),
                ),
              ),
            ),
          ),
        );
      }
    }
    
    class ItemsScene extends StatefulWidget {
      @override
      _ItemsSceneState createState() => _ItemsSceneState();
    }
    
    class _ItemsSceneState extends State<ItemsScene> {
      List<ItemModel> items = [
        ItemModel(offset: Offset(70, 100), text: 'text1'),
        ItemModel(offset: Offset(200, 100), text: 'text2'),
        ItemModel(offset: Offset(200, 230), text: 'text3'),
      ];
    
      Function onDragStart(int index) => (x, y) {
            setState(() {
              items[index] = items[index].copyWithNewOffset(Offset(x, y));
            });
          };
    
      @override
      Widget build(BuildContext context) {
        return Stack(
          children: <Widget>[
            CustomPaint(
              size: Size(double.infinity, double.infinity),
              painter: CurvedPainter(
                offsets: items.map((item) => item.offset).toList(),
              ),
            ),
            ..._buildItems()
          ],
        );
      }
    
      List<Widget> _buildItems() {
        final res = <Widget>[];
        items.asMap().forEach((ind, item) {
          res.add(_Item(
            onDragStart: onDragStart(ind),
            offset: item.offset,
            text: item.text,
          ));
        });
    
        return res;
      }
    }
    
    class _Item extends StatelessWidget {
      _Item({
        Key key,
        this.offset,
        this.onDragStart,
        this.text,
      });
    
      final double size = 100;
      final Offset offset;
      final Function onDragStart;
      final String text;
    
      _handleDrag(details) {
        print(details);
        var x = details.globalPosition.dx;
        var y = details.globalPosition.dy;
        onDragStart(x, y);
      }
    
      @override
      Widget build(BuildContext context) {
        return Positioned(
          left: offset.dx - size / 2,
          top: offset.dy - size / 2,
          child: GestureDetector(
            onPanStart: _handleDrag,
            onPanUpdate: _handleDrag,
            child: Container(
              width: size,
              height: size,
              child: Text(text),
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border.all(
                  color: Colors.blueAccent,
                ),
              ),
            ),
          ),
        );
      }
    }
    
    class CurvedPainter extends CustomPainter {
      CurvedPainter({this.offsets});
    
      final List<Offset> offsets;
    
      @override
      void paint(Canvas canvas, Size size) {
        if (offsets.length > 1) {
          offsets.asMap().forEach((index, offset) {
            if (index == 0) return;
            canvas.drawLine(
              offsets[index - 1],
              offsets[index],
              Paint()
                ..color = Colors.red
                ..strokeWidth = 2,
            );
          });
        }
      }
    
      @override
      bool shouldRepaint(CurvedPainter oldDelegate) => true;
    }
    
    class ItemModel {
      ItemModel({this.offset, this.text});
    
      final Offset offset;
      final String text;
    
      ItemModel copyWithNewOffset(Offset offset) {
        return ItemModel(offset: offset, text: text);
      }
    }
    

    【讨论】:

    • 感谢您提供的优秀示例代码!我用它制作了一个 codepen,以防有人想尝试交互式版本:codepen.io/maks-the-lessful/pen/OJMLZav
    • @Kherel wooooooooow,太棒了,谢谢我的家伙
    猜你喜欢
    • 2020-05-09
    • 2021-08-19
    • 2021-08-07
    • 2018-09-13
    • 1970-01-01
    • 2020-07-29
    • 2013-09-08
    • 1970-01-01
    相关资源
    最近更新 更多