flutter - flutter 有向图。我可以将CustomPainter类与自定义窗口小部件一起使用吗?

标签 flutter dart flutter-layout

我想像下面的图片一样用 flutter 建立有向图。
我不知道从哪里开始。我在互联网上搜索失败。我需要哪种图形的图表?
我试图用自定义的画家类来建立这个图。我不知道如何在自定义画家类中使用自定义小部件。 (例如,旁边有人物图片和文字的rect)。
我只能画矩形和线条...
缩放和平移我认为我可以使用GestureDetector类。
该图应可动态定制。

enter image description here

最佳答案

您需要拆分任务。

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

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

    更新:
    codepen interactive version由@maks创建

    enter image description here
    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);
      }
    }
    

    关于flutter - flutter 有向图。我可以将CustomPainter类与自定义窗口小部件一起使用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60596300/

    相关文章:

    android - flutter 中的框架布局替代方案

    flutter - 如何实时收听 TextField 更改并使用 TextField 内容更新 Text 小部件?

    flutter - 我应该总是在我的 flutter 屏幕的顶层使用 SafeArea 吗?

    flutter - 创建多个初始路径

    flutter - 无法更改 MaterialButton 的宽度

    json - 如何使用另一个类和JsonKey排除DART Model中的字段?

    dart - Flutter 获取电话号码

    dart - Flutter:我的对话框键盘溢出

    flutter - 错误 flutter NotificationDetails 位置参数太多

    flutter - 如何使 FloatingActionButton 在 persistentFooterButtons 中居中