Integrate Skill Tree Functionality into Main Application (#5189)

This commit is contained in:
hunteraraujo
2023-09-10 15:07:54 -07:00
committed by GitHub
12 changed files with 306 additions and 12 deletions

View File

@@ -1,4 +1,5 @@
import 'package:auto_gpt_flutter_client/viewmodels/api_settings_viewmodel.dart';
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
import 'package:flutter/material.dart';
import 'views/main_layout.dart';
import 'package:provider/provider.dart';
@@ -55,6 +56,9 @@ class MyApp extends StatelessWidget {
create: (context) => ChatViewModel(chatService)),
ChangeNotifierProvider(
create: (context) => TaskViewModel(taskService)),
ChangeNotifierProvider(
create: (context) => SkillTreeViewModel(),
),
],
child: MainLayout(),
),

View File

@@ -0,0 +1,25 @@
class Ground {
final String answer;
final List<String> shouldContain;
final List<String> shouldNotContain;
final List<String> files;
final Map<String, dynamic> eval;
Ground({
required this.answer,
required this.shouldContain,
required this.shouldNotContain,
required this.files,
required this.eval,
});
factory Ground.fromJson(Map<String, dynamic> json) {
return Ground(
answer: json['answer'],
shouldContain: List<String>.from(json['should_contain']),
shouldNotContain: List<String>.from(json['should_not_contain']),
files: List<String>.from(json['files']),
eval: json['eval'],
);
}
}

View File

@@ -0,0 +1,19 @@
class Info {
final String difficulty;
final String description;
final List<String> sideEffects;
Info({
required this.difficulty,
required this.description,
required this.sideEffects,
});
factory Info.fromJson(Map<String, dynamic> json) {
return Info(
difficulty: json['difficulty'],
description: json['description'],
sideEffects: List<String>.from(json['side_effects']),
);
}
}

View File

@@ -0,0 +1,34 @@
import 'package:auto_gpt_flutter_client/models/skill_tree/ground.dart';
import 'package:auto_gpt_flutter_client/models/skill_tree/info.dart';
class SkillNodeData {
final String name;
final List<String> category;
final String task;
final List<String> dependencies;
final int cutoff;
final Ground ground;
final Info info;
SkillNodeData({
required this.name,
required this.category,
required this.task,
required this.dependencies,
required this.cutoff,
required this.ground,
required this.info,
});
factory SkillNodeData.fromJson(Map<String, dynamic> json) {
return SkillNodeData(
name: json['name'],
category: List<String>.from(json['category']),
task: json['task'],
dependencies: List<String>.from(json['dependencies']),
cutoff: json['cutoff'],
ground: Ground.fromJson(json['ground']),
info: Info.fromJson(json['info']),
);
}
}

View File

@@ -0,0 +1,23 @@
class SkillTreeEdge {
final String id;
final String from;
final String to;
final String arrows;
SkillTreeEdge({
required this.id,
required this.from,
required this.to,
required this.arrows,
});
// Optionally, add a factory constructor to initialize from JSON
factory SkillTreeEdge.fromJson(Map<String, dynamic> json) {
return SkillTreeEdge(
id: json['id'],
from: json['from'],
to: json['to'],
arrows: json['arrows'],
);
}
}

View File

@@ -0,0 +1,18 @@
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_node_data.dart';
// TODO: Update this with actual data
class SkillTreeNode {
final String color;
final int id;
// final SkillNodeData data;
SkillTreeNode({required this.color, required this.id});
factory SkillTreeNode.fromJson(Map<String, dynamic> json) {
return SkillTreeNode(
color: json['color'],
id: json['id'],
);
}
}

View File

@@ -0,0 +1,76 @@
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_edge.dart';
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_node.dart';
import 'package:flutter/foundation.dart';
import 'package:graphview/GraphView.dart';
class SkillTreeViewModel extends ChangeNotifier {
List<SkillTreeNode> _skillTreeNodes = [];
List<SkillTreeEdge> _skillTreeEdges = [];
SkillTreeNode? _selectedNode;
SkillTreeNode? get selectedNode => _selectedNode;
final Graph graph = Graph()..isTree = true;
BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();
void initializeSkillTree() {
_skillTreeNodes = [];
_skillTreeEdges = [];
// Add nodes to _skillTreeNodes
_skillTreeNodes.addAll([
SkillTreeNode(color: 'red', id: 1),
SkillTreeNode(color: 'blue', id: 2),
SkillTreeNode(color: 'green', id: 3),
SkillTreeNode(color: 'yellow', id: 4),
SkillTreeNode(color: 'orange', id: 5),
SkillTreeNode(color: 'purple', id: 6),
SkillTreeNode(color: 'brown', id: 7),
SkillTreeNode(color: 'pink', id: 8),
SkillTreeNode(color: 'grey', id: 9),
SkillTreeNode(color: 'cyan', id: 10),
SkillTreeNode(color: 'magenta', id: 11),
SkillTreeNode(color: 'lime', id: 12)
]);
// Add edges to _skillTreeEdges
_skillTreeEdges.addAll([
SkillTreeEdge(id: '1_to_2', from: '1', to: '2', arrows: 'to'),
SkillTreeEdge(id: '1_to_3', from: '1', to: '3', arrows: 'to'),
SkillTreeEdge(id: '1_to_4', from: '1', to: '4', arrows: 'to'),
SkillTreeEdge(id: '2_to_5', from: '2', to: '5', arrows: 'to'),
SkillTreeEdge(id: '2_to_6', from: '2', to: '6', arrows: 'to'),
SkillTreeEdge(id: '6_to_7', from: '6', to: '7', arrows: 'to'),
SkillTreeEdge(id: '6_to_8', from: '6', to: '8', arrows: 'to'),
SkillTreeEdge(id: '4_to_9', from: '4', to: '9', arrows: 'to'),
SkillTreeEdge(id: '4_to_10', from: '4', to: '10', arrows: 'to'),
SkillTreeEdge(id: '4_to_11', from: '4', to: '11', arrows: 'to'),
SkillTreeEdge(id: '11_to_12', from: '11', to: '12', arrows: 'to')
]);
builder
..siblingSeparation = (100)
..levelSeparation = (150)
..subtreeSeparation = (150)
..orientation = (BuchheimWalkerConfiguration.ORIENTATION_LEFT_RIGHT);
notifyListeners();
}
void toggleNodeSelection(int nodeId) {
if (_selectedNode?.id == nodeId) {
// Unselect the node if it's already selected
_selectedNode = null;
} else {
// Select the new node
_selectedNode = _skillTreeNodes.firstWhere((node) => node.id == nodeId);
}
notifyListeners();
}
// Getter to expose nodes for the View
List<SkillTreeNode> get skillTreeNodes => _skillTreeNodes;
// Getter to expose edges for the View
List<SkillTreeEdge> get skillTreeEdges => _skillTreeEdges;
}

View File

@@ -1,3 +1,4 @@
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
import 'package:auto_gpt_flutter_client/viewmodels/task_viewmodel.dart';
import 'package:auto_gpt_flutter_client/viewmodels/chat_viewmodel.dart';
import 'package:auto_gpt_flutter_client/views/side_bar/side_bar_view.dart';
@@ -23,6 +24,9 @@ class MainLayout extends StatelessWidget {
// Access the ChatViewModel from the context
final chatViewModel = Provider.of<ChatViewModel>(context);
// Access the ChatViewModel from the context
final skillTreeViewModel = Provider.of<SkillTreeViewModel>(context);
// Check the screen width and return the appropriate layout
if (width > 800) {
// For larger screens, return a side-by-side layout
@@ -36,7 +40,8 @@ class MainLayout extends StatelessWidget {
return SizedBox(
width: 280, child: TaskView(viewModel: taskViewModel));
} else {
return const Expanded(child: SkillTreeView());
return Expanded(
child: SkillTreeView(viewModel: skillTreeViewModel));
}
},
),

View File

@@ -1,20 +1,69 @@
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
import 'package:auto_gpt_flutter_client/views/skill_tree/tree_node_view.dart';
import 'package:flutter/material.dart';
import 'package:graphview/GraphView.dart';
class SkillTreeView extends StatelessWidget {
const SkillTreeView({super.key});
class SkillTreeView extends StatefulWidget {
final SkillTreeViewModel viewModel;
const SkillTreeView({Key? key, required this.viewModel}) : super(key: key);
@override
_TreeViewPageState createState() => _TreeViewPageState();
}
class _TreeViewPageState extends State<SkillTreeView> {
@override
void initState() {
super.initState();
widget.viewModel.initializeSkillTree();
// Create Node and Edge objects for GraphView
final Map<int, Node> nodeMap = {};
for (var skillTreeNode in widget.viewModel.skillTreeNodes) {
final node = Node.Id(skillTreeNode.id);
widget.viewModel.graph.addNode(node);
nodeMap[skillTreeNode.id] = node;
}
for (var skillTreeEdge in widget.viewModel.skillTreeEdges) {
final fromNode = nodeMap[int.parse(skillTreeEdge.from)];
final toNode = nodeMap[int.parse(skillTreeEdge.to)];
widget.viewModel.graph.addEdge(fromNode!, toNode!);
}
}
@override
Widget build(BuildContext context) {
return Container(
color: Colors.blue[100], // Background color
child: const Center(
child: Text(
'SkillTreeView',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: InteractiveViewer(
constrained: false,
boundaryMargin: EdgeInsets.all(100),
minScale: 0.01,
maxScale: 5.6,
child: GraphView(
graph: widget.viewModel.graph,
algorithm: BuchheimWalkerAlgorithm(widget.viewModel.builder,
TreeEdgeRenderer(widget.viewModel.builder)),
paint: Paint()
..color = Colors.green
..strokeWidth = 1
..style = PaintingStyle.stroke,
builder: (Node node) {
int nodeId = node.key?.value as int;
return TreeNodeView(
nodeId: nodeId,
selected: nodeId == widget.viewModel.selectedNode?.id);
},
),
),
),
),
],
),
);
}

View File

@@ -0,0 +1,32 @@
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class TreeNodeView extends StatelessWidget {
final int nodeId;
final bool selected;
TreeNodeView({required this.nodeId, this.selected = false});
@override
Widget build(BuildContext context) {
return InkWell(
onTap: () {
print('Node $nodeId clicked');
Provider.of<SkillTreeViewModel>(context, listen: false)
.toggleNodeSelection(nodeId);
},
child: Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: selected ? Colors.red : Colors.white,
borderRadius: BorderRadius.circular(4),
boxShadow: [
BoxShadow(color: Colors.red, spreadRadius: 1),
],
),
child: Text('Node $nodeId'),
),
);
}
}

View File

@@ -104,6 +104,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
graphview:
dependency: "direct main"
description:
name: graphview
sha256: bdba183583b23c30c71edea09ad5f0beef612572d3e39e855467a925bd08392f
url: "https://pub.dev"
source: hosted
version: "1.2.0"
highlight:
dependency: transitive
description:

View File

@@ -39,6 +39,7 @@ dependencies:
provider: ^6.0.5
http: ^1.1.0
shared_preferences: ^2.2.1
graphview: ^1.2.0
dev_dependencies:
flutter_test: