Flutter 颤振有向图。我可以将CustomPainter类与自定义小部件一起使用吗?
我想用flatter构建一个有向图,如下图所示。 我不知道从哪里开始。我在网上搜索没有成功。这种图形需要哪些算法? 我试图用自定义的painter类构建这个图。我不知道如何在自定义画师类中使用自定义小部件。(例如,带有人物图片和文本的矩形)。 我只能画直线和直线。。。 缩放和平移我想我可以用GestureDetector类。 图表应该是可动态定制的Flutter 颤振有向图。我可以将CustomPainter类与自定义小部件一起使用吗?,flutter,dart,flutter-layout,flutter-canvas,Flutter,Dart,Flutter Layout,Flutter Canvas,我想用flatter构建一个有向图,如下图所示。 我不知道从哪里开始。我在网上搜索没有成功。这种图形需要哪些算法? 我试图用自定义的painter类构建这个图。我不知道如何在自定义画师类中使用自定义小部件。(例如,带有人物图片和文本的矩形)。 我只能画直线和直线。。。 缩放和平移我想我可以用GestureDetector类。 图表应该是可动态定制的 CustomPainter似乎有些过分了。使用堆栈。您需要首先计算每个节点或连接的位置和大小,然后使用定位小部件放置它们。您需要拆分任务 使层缩放和
CustomPainter似乎有些过分了。使用堆栈。您需要首先计算每个节点或连接的位置和大小,然后使用定位小部件放置它们。您需要拆分任务
导入“包装:颤振/材料.省道”;
void main()=>runApp(MyApp());
类MyApp扩展了无状态小部件{
@凌驾
小部件构建(构建上下文){
返回材料PP(
家:脚手架(
正文:中(
子:容器(
对齐:对齐.center,
子项:itemscene(),
装饰:盒子装饰(
边界:边界(
颜色:Colors.blueAccent,
),
),
),
),
),
);
}
}
类itemscene扩展StatefulWidget{
@凌驾
_ItemsCensState createState()=>\u ItemsCensState();
}
类_itemscenstate扩展状态{
列表项=[
ItemModel(偏移量:偏移量(70100),文本:“text1”),
ItemModel(偏移量:偏移量(200100),文本:“text2”),
ItemModel(偏移量:偏移量(200230),文本:“text3”),
];
函数onDragStart(int索引)=>(x,y){
设置状态(){
items[索引]=items[索引].copyWithNewOffset(偏移量(x,y));
});
};
@凌驾
小部件构建(构建上下文){
返回堆栈(
儿童:[
定制油漆(
大小:大小(双无限,双无限),
画家:曲线画家(
偏移量:items.map((item)=>item.offset.toList(),
),
),
…_buildItems()
],
);
}
列表_buildItems(){
最终res=[];
items.asMap().forEach((ind,item){
res.add(_项(
onDragStart:onDragStart(ind),
抵销:item.offset,
text:item.text,
));
});
返回res;
}
}
类_项扩展了无状态小部件{
_项目({
关键点,
这个,抵消,,
这是一个开始,
这个文本,
});
最终双尺寸=100;
最终偏移量;
最终功能启动;
最终字符串文本;
_手抹布(细节){
印刷(详情);
var x=details.globalPosition.dx;
变量y=details.globalPosition.dy;
onDragStart(x,y);
}
@凌驾
小部件构建(构建上下文){
返回定位(
左:offset.dx-大小/2,
顶部:offset.dy-大小/2,
儿童:手势检测器(
onPanStart:_handleDrag,
onPanUpdate:_handleDrag,
子:容器(
宽度:大小,
高度:尺寸,
子:文本(Text),
装饰:盒子装饰(
颜色:颜色,白色,
边界:边界(
颜色:Colors.blueAccent,
),
),
),
),
);
}
}
类CurvedPainter扩展了CustomPainter{
CurvedPainter({this.offset});
最终列表偏移量;
@凌驾
空心油漆(帆布,尺寸){
如果(偏移量.长度>1){
offset.asMap().forEach((索引,偏移量){
如果(指数=0)返回;
帆布拉丝(
偏移量[索引-1],
偏移量[索引],
油漆()
…颜色=颜色。红色
..冲程宽度=2,
);
});
}
}
@凌驾
布尔应该重新绘制(CurvedPainter oldDelegate)=>真;
}
类项模型{
ItemModel({this.offset,this.text});
最终偏移量;
最终字符串文本;
ItemModel copyWithNewOffset(偏移量偏移量){
返回ItemModel(偏移量:偏移量,文本:文本);
}
}
节点间关系的逻辑是什么?您是否可以发布您使用自定义painter类编写的代码,正如您已经尝试过的那样。。!还有节点在图中放置方式背后的逻辑。否则,这个问题似乎过于宽泛,看起来像是要求一个共同工作的实现。以我的拙见,在flutter中使用自定义画师绝对可以实现这一点。感谢您提供的优秀示例代码!我用它做了一个密码笔,以防有人想要一个交互式版本来试用:@Kherel-woooooo,太棒了,谢谢我的兄弟
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);
}
}