Closed shptecheu closed 2 years ago
Oh man, I'm sorry, this package is not ready for production. I thought I put it in package description but I might not have.
I'm stuck on a really low level problem but once I solve that it should be relatively easy to finish the rest.
Yeah, I'll likely unlist this package so I don't accidentally trick more people.
No harm done. I was looking for a package with similar features and thought I'd give it a shot. Looks like a neat idea.
Anything I might be able to help with?
TLDR: Yeah, no worries about immediate help unless you want to. I use a similar skill tree thing in my personal app so I'll continue work on this indefinitely.
Yeah ! I'd love help and even if you can't directly help I'd still love to explain my current issues and architecture. I always think it's nice to talk stuff through.
Take for example a user wanting to create a skill tree similar to the Borderlands or WoW. The really only need five fields
Where the delegate type defines the graph type and the render layout. For example if they pass through a RadialTreeDelegate
, we'd then use a RadialGraph
and a RenderRadialLayout
to create the CustomRenderObject
.
@override
RenderObject createRenderObject(BuildContext context) {
final _delegate = delegate;
if (_delegate is DirectedTreeDelegate<IdType>) {
return RenderDirectedTree<EdgeType, NodeType, IdType>(
graph: graph,
delegate: _delegate,
);
} else if (_delegate is RadialTreeDelegate<IdType>) {
return RenderRadialLayout<EdgeType, NodeType, IdType>(
graph: graph,
delegate: _delegate,
);
}
}
The delegate would hold information specific to the layout and paint if needed.
Notice the graph object? That would be created from edges
and nodes
. I explicitly make the distinction between the rendering world and the abstract graph theory world.
Render Specific | Graph Specific |
---|---|
SkillEdge | Edge |
SkillNode | Node |
A graph would in theory have all the graph specific methods and information available matching the graph type. For example, if we were rendering an acyclic graph we could have the depth first search implemented there which we would then use in the RenderObject to draw things.
Really, the RenderObject only concerns itself though with SkillEdges and SkillNodes as those are the "real" children drawn to the screen
You can think of SkillEdge and SkillNode as widgets that simply implement their counterparts and have configuration specific to how they'll be drawn.
My stopping point has primarily been in how I separate out the SkillNode
s from the SkillEdge
s inside the RenderObject class:
class RenderDirectedTree<EdgeType, NodeType extends Object,
IdType extends Object> extends RenderSkillTree<EdgeType, NodeType, IdType> {
RenderDirectedTree({
required Graph<EdgeType, NodeType, IdType> graph,
required this.delegate,
}) : super(graph: graph, delegate: delegate);
@override
final DirectedTreeDelegate<IdType> delegate;
@override
void performLayout() {
// Could possibly be a SkillNode or a SkillEdge
final children = getChildrenAsList();
}
}
You cannot do a simple if (child is type)
as they seem to be abstract render things rather than their type I passed in. I asked creativecreatorormaybenot and this was the method he suggested but yeah I hit a little snare here.
Once I have them separated, it'd be a simple thing to split up the performLayout
into one for drawing the nodes (via the nodeBuilder I specified earlier) and afterward, when you have all nodes boxes and positioning, drawing edges (edgeBuilder). This drawing and rendering happening in one renderObject would be rather flexible and you'd have all the sizing information you'd need to make complex graph representations.
Sorry if this explanation was bloated and unnecessary. I think I needed to talk out the problem so I could continue working on this.
Resources:
If you're confused on renderObjects, you can become an expert very quickly through this video
https://www.youtube.com/watch?v=HqXNGawzSbY
Also, I haven't looked too closely so as to not end up copying implementations but graphview is a great lib for doing similar graph stuff. It might even be what you want.
Thanks for the context. I have to say, I am no expert in flutter, so I need to catch up on the resources attached. Maybe I'll be in a better position to provide assistance :)
So beside some weird edge
bugs going on, I've taken a big step in drawing edges ! 🎉
The nice thing about the way I'm handling edges is that the points at their ends will be able to be regular widgets -- which means we'll be able to have drag nodes. I plan to implement dragging and reconnecting edge ends to different nodes as a default in the package (behind a flag to disable it maybe).
I'll come back and explain what I did to get the edges working at a later time but I just wanted to share the news as I'm very excited.
I'd say a couple more weeks before I publish but I like the way it's going. THanks for the interest in the package guys.
Also, let me know if you all have any idea how edge drawing is usually handled. I'm thinking of just using a quadratic bezier and a couple of arbitrary control points to connect two nodes but I'm just making it up as I go.
looks like great progress!
FYI, I just discovered an offical package dealing with graphs: https://pub.dev/packages/graphs/
Yeah, I saw that package early on and tried it out actually but I wasn't really sure what it was adding at the time. They don't have a lot of examples and their graph model didn't really look like it matched mine. Until I hit a roadblock where I need an advanced graph algorithms I think I'll leave it.
Anyways ! I wanted to share my effort and show you something I made with the package. Left is WoW and right is mine.
I still haven't totally figured out how to properly draw edges but I did make a nice builder for them with a robust signature I think:
typedef EdgePainter = void Function({
required Offset toNodeCenter,
required Offset fromNodeCenter,
required List<Rect> allNodeRects,
required List<Rect> intersectingNodeRects,
required Canvas canvas,
});
where allNodeRects is all the nodes drawn on the screen and intersectingNodeRects are those node rects which are intercepted by a straight line from toNodeCenter
to fromNodeCenter
. I'm hoping to allow for a good default edgeBuilder that handles corners decently though.
Another concern I have is how I'm going to serialize this entire graph. I've taken into consideration serializing edges and nodes but I'll also need to consider layouts. Layouts or delegates might need their own serialization builder. So far, I've only concerned myself with the layered model and this is what I think it'll look like although I'm open to suggestions:
{
"nodes": {
"0": null,
"1": null,
"2": null,
"3": null,
"4": null,
"5": null,
"6": null
},
"edges": {
"0-1": null,
"2-5": null,
"1-3": null
},
"layout": [
[
"0",
"1",
null
],
[
"2",
null,
"3"
],
[
"4",
"5",
"6"
]
]
}
The null
values would be relpaced with edgeData or nodeData if provided.
Once I improve what I have currently for layered layouts I'll tackle another skill tree type to allow me to better genericize (?) the package. Maybe something like this? I dunno
[EDIT] Sorry I've been piggy backing off your issue ! Feel free to close and I'll move further updates to a github discussion template
I'm near done with the initial release. I've got a LOT of TODO
s for myself so it's definitely a work in progress.
The docs aren't up to date either. Will have to do a full sweep through everything once more before I announce.
Reproducible steps:
Create new Flutter project flutter pub add skill_tree
Error message below:
Because every version of flutter_test from sdk depends on meta 1.3.0 and skill_tree depends on meta ^1.7.0, flutter_test from sdk is forbidden. So, because skill_tree depends on flutter_test any from sdk, version solving failed.
flutter doctor
Doctor summary (to see all details, run flutter doctor -v): Flutter (Channel stable, 2.2.3, on Microsoft Windows [Version 10.0.19043.1165], locale en-GB)