stargazing-dino / skill_tree

A package to create skill trees for Flutter apps
MIT License
7 stars 2 forks source link

Cannot install package in new flutter project #3

Closed shptecheu closed 2 years ago

shptecheu commented 3 years ago

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)

stargazing-dino commented 3 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.

camillerimichael commented 3 years ago

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?

stargazing-dino commented 3 years ago

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

Screen Shot 2021-09-03 at 7 18 03 AM

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 SkillNodes from the SkillEdges 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.

shptecheu commented 3 years ago

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 :)

stargazing-dino commented 3 years ago

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.

shptecheu commented 3 years ago

looks like great progress!

FYI, I just discovered an offical package dealing with graphs: https://pub.dev/packages/graphs/

stargazing-dino commented 3 years ago

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.

126853484-7b1334a4-9ac9-4d6e-89b5-3717d508c418         126853484-7b1334a4-9ac9-4d6e-89b5-3717d508c418

Code ```dart class LayeredExamplePage extends StatelessWidget { const LayeredExamplePage({Key? key}) : super(key: key); @override Widget build(BuildContext context) { final theme = Theme.of(context); final seed = Random().nextInt(5000); return Stack( fit: StackFit.passthrough, children: [ Image.asset( 'assets/ruined_city.png', fit: BoxFit.cover, ), Scaffold( backgroundColor: theme.primaryColor.withAlpha(20), appBar: AppBar( systemOverlayStyle: SystemUiOverlayStyle.light, title: const Text( 'Skill Tree', style: TextStyle(color: Colors.white), ), ), body: Padding( padding: const EdgeInsets.all(16.0), child: SkillTree( delegate: LayeredTreeDelegate( mainAxisSpacing: 32.0, crossAxisSpacing: 48.0, layout: [ ['0', '1', '2', null], ['3', '4', '5', null], ['6', '7', '8', null], [null, '9', '10', null], ['11', '12', null, '13'], [null, null, '14', null], [null, '15', '16', null], ], ), nodeBuilder: (node) { final photoId = int.parse(node.id) + 1; return SkillNode.fromNode( node: node, child: Center( child: Item( photoNumber: photoId, seed: seed, ), ), ); }, edges: const [ Edge(from: '7', to: '9'), Edge(from: '10', to: '14'), Edge(from: '12', to: '15'), // Edge(from: '12', to: '13'), ], nodes: const [ Node(id: '0'), Node(id: '1'), Node(id: '2'), Node(id: '3'), Node(id: '4'), Node(id: '5'), Node(id: '6'), Node(id: '7'), Node(id: '8'), Node(id: '9'), Node(id: '10'), Node(id: '11'), Node(id: '12'), Node(id: '13'), Node(id: '14'), Node(id: '15'), Node(id: '16'), ], ), ), ), ], ); } } ```

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

Outriders-Trickster-Class-Tree-Screenshot

[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

stargazing-dino commented 2 years ago

I'm near done with the initial release. I've got a LOT of TODOs 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.

ezgif-1-56d7eb6e7498