skill_tree 0.0.1+1 skill_tree: ^0.0.1+1 copied to clipboard
A package for creating skill trees of any shape and size
skill_tree #
🚧 Do not use. This package is in production. 🚧 #
A package to build skill trees of any kind. This package differs from graphview
as it only tries to provide users an interface to make a skill tree rather than a general purpose graph viewer.
Simple usage #
class Home extends StatelessWidget {
const Home({ Key? key }) : super(key: key);
@override
Widget build(BuildContext context) {
return SkillTree<void, void>(
delegate: LayeredTreeDelegate(),
edges: [
Edge(from: '0', to: '1'),
Edge(from: '0', to: '2'),
],
nodes: [
Node(id: '0'),
Node(id: '2'),
Node(id: '1'),
],
);
}
}
Why not
Map<Node, Set<Node>> edges
?Answer: it's easier to serialize and deserialize this way... I think -- also, this way you can attach data to edges
Say you want to store information on the node that is specific to your application. For example, the MP it costs to fire that skill. For this, you can provide a map or data class to hold your custom data. To do this, you'll also need to provide a custom serializer and deserializer.
class MyData {
const MyData(this.cost);
final int cost;
}
class Home extends StatelessWidget {
const Home({ Key? key }) : super(key: key);
@override
Widget build(BuildContext context) {
// The first data type is the data type of the edge. The second is
// of the node
return SkillTree<void, MyData>(
delegate: LayeredTreeDelegate(),
serializeNode: (MyData myData) {
return myData.toJson(myData);
},
deserializeNode: (Map<String, dynamic> json) {
return myData.fromJson(json);
},
edges: [
Edge(from: '0', to: '1'),
Edge(from: '0', to: '2'),
],
nodes: [
Node(id: '0', data: MyData(4)),
Node(id: '2', data: MyData(4)),
Node(id: '1', data: MyData(4)),
],
);
}
}
Now that you have your MP on the node you can render it how you like by providing a nodeBuilder. It will receive the node and your data.
nodeBuilder: (node) {
return SkillNode.fromNode(
node: node,
child: Column(
children: [
Text(node.name),
Text('MP cost: ${node.data.cost}'),
],
),
);
},
That's pretty shweet right? Notice the SkillNode
though? That's because you have the option to insert SkillNode
s inside of the nodes field directly and define your children that way. The same is possible with SkillEdge
.
SkillTree<void, void>(
nodes: [
SkillNode(id: '0', child: Container()),
// ...
],
),
Another feature users usually want with their skill trees are points and points to acqure functionality. A node usually has a cost
associated with it to unlock. For that, you can provide a requirement
to your SkillNode
and a value
to your SkillTree
. Note, everything defined on a SkillTree
is automatically deserialized for you excluding the data
. With that information, you can decide on whether your node is locked or unlocked.
class Home extends StatelessWidget {
const Home({ Key? key }) : super(key: key);
@override
Widget build(BuildContext context) {
return SkillTree<void, void>(
value: 5,
maxValue: 20,
delegate: LayeredTreeDelegate(),
edges: [
Edge(from: '0', to: '1'),
Edge(from: '0', to: '2'),
],
nodes: [
// TODO: I need an assertion in the graph that checks a SkillNode has a
// smaller requirement than its descendant. Throw [GraphException].
SkillNode(id: '0', child: MyChild(), requirement: 1),
SkillNode(id: '1', child: MyChild(), requirement: 10),
SkillNode(id: '2', child: MyChild(), requirement: 15),
],
);
}
}
But muh customizability ! #
skill_tree
provides a theme called SkillTreeThemeData
that you can modify as you like. If none is provided, a default is created that takes values from your app's default ThemeData
.
Custom edges can even be drawn by providing an edgePainter
to either a SkillTree
or a SkillEdge
.
Help me #
I'm not very good with graphs. I'd love to have fancy algorithms that draw both the nodes and edges but I think I'll need support for that.
Ways to help:
- Open an issue with an example of a skill tree and a rough sketch of how it'd be accomplished. We can start with two things. How the
graph
representation would look like and how alayout
algorithm would roughly work. You don't have to be exact. - Improve on my definition of a graph and its subtypes.
- Provide a better interface for managing the graph. This could be writing me some generic algorithms like depthFirstSearh or whatever.
- Every TODO: is free game but are likely related to low level Flutter rendering
TODO #
- toggleable GUI to add and edit nodes (onAddChild, onUpdate)
- animations
- unnattached nodes
- SkillTree.fromJson
- Other serialization options (adjacency matrix?)