颤振有向图。我可以将CustomPainter类与自定义窗口小部件一起使用吗? [英] Flutter directed graph. Can I use CustomPainter Class with custom widgets?

查看:70
本文介绍了颤振有向图。我可以将CustomPainter类与自定义窗口小部件一起使用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



解决方案

您需要拆分任务。


  1. 制作图层以缩放和移动整个场景,则可以将
    GestureDetector窗口小部件与onScale事件+ Transform.scale窗口小部件
    (检查zoom_widget包)一起使用。

  2. 使单个项目可拖动。使用GestureDetector + onPan事件。

  3. 使用CustomPainter在元素之间绘制连接线。我已经用直线显示了主要逻辑。

..添加了额外的逻辑来添加新项目。



更新:

  import'package:flutter / material.dart'; 

void main()=> runApp(MyApp());

类MyApp扩展了StatelessWidget {
@override
Widget build(BuildContext context){
return MaterialApp(
home:Scaffold(
body :Center(
子项:Container(
对齐方式:Alignment.center,
子项:ItemsScene(),
装饰:BoxDecoration(
边界:Border.all(
颜色:Colors.blueAccent,
),
),
),
),
),
);
}
}

类ItemsScene扩展了StatefulWidget {
@override
_ItemsSceneState createState()=> _ItemsSceneState();
}

class _ItemsSceneState扩展了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'),
];

函数onDragStart(int index)=> (x,y){
setState((){
items [index] = items [index] .copyWithNewOffset(Offset(x,y));
});
};

@override
小部件构建(BuildContext上下文){
return Stack(
children:< Widget> [
CustomPaint(
size :Size(double.infinity,double.infinity),
画家: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,
));
});

返回资源;
}
}

类_Item扩展StatelessWidget {
_Item({
密钥,
this.offset,
this .onDragStart,
this.text,
});

最终双倍大小= 100;
最终偏移量;
final Function onDragStart;
最终字符串文本;

_handleDrag(details){
print(details ;;
var x = details.globalPosition.dx;
var y = details.globalPosition.dy;
onDragStart(x,y);
}

@override
窗口小部件build(BuildContext context){
return Positioned(
left:offset.dx-size / 2,
顶部:offset.dy-大小/ 2,
子对象:GestureDetector(
onPanStart:_handleDrag,
onPanUpdate:_handleDrag,
子对象:容器(
宽度:大小,
高度:大小,
子级:Text(text),
装饰:BoxDecoration(
颜色:Colors.white,
边框:Border.all(
颜色:Colors.blueAccent,
),
),
),
),
);
}
}

类CurvedPainter扩展了CustomPainter {
CurvedPainter({this.offsets});

最终列表< Offset>抵消

@override
void paint(画布画布,大小大小){
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)=>真正;
}

class ItemModel {
ItemModel({this.offset,this.text});

最终偏移量;
最终字符串文本;

ItemModel copyWithNewOffset(Offset offset){
return ItemModel(offset:offset,text:text);
}
}


I want to build a directed graph like in the picture below with flutter. I dont know where to start. I searched at internet without success. Which algorihms do I need for this kind of graph? I tried to build this graph with custom painter class. I dont know how to use custom widgets inside custom painter class. (for example a rect with a picture of person and text beside). I only was able to draw rect and lines... Zoom and pan I thought I can do with GestureDetector class. The graph should be customizable dynamicly.

解决方案

You need to split your tasks.

  1. Make the layer to zoom and move whole scene, you can use the GestureDetector widget with onScale events + Transform.scale widget, (check zoom_widget package).
  2. Make the single item draggable. Use GestureDetector + onPan events.
  3. Draw connection lines between element using CustomPainter. I've made direct lines to show the main logic.

.. add extra logic how to add new items.

Update: codepen interactive version created by @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);
  }
}

这篇关于颤振有向图。我可以将CustomPainter类与自定义窗口小部件一起使用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆