import 'package:graphs/src/graph.dart'; import 'package:graphs/src/curve_painter.dart'; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; Graphs getGraph() { List d = []; d.add(Dot.fromTwoLists("1", [2], [3])); d.add(Dot.fromTwoLists("2", [3], [1])); d.add(Dot.fromTwoLists("3", [1], [2])); //d.add(Dot.fromTwoLists("Name1", [], [])); //d.add(Dot.fromTwoLists("Name2", [], [])); return Graphs.fromList("Имя графа", d, true, true); } class DrawingPage extends StatefulWidget { const DrawingPage({Key? key}) : super(key: key); @override State createState() => _DrawingPageState(); } class _DrawingPageState extends State { double screenSize = 0; Graphs graphData = getGraph(); List? intListPath; List? dfsAccessTable; //List? dijkstraTable; String op = Operations.none; int? startDot; int? endDot; String? dropdownValue1; String? dropdownValue2; String currOp = ""; final _textNameController = TextEditingController(); final _textLnthController = TextEditingController(); final _textGrNmController = TextEditingController(); void clearInputData() { setState(() { _textLnthController.clear(); _textNameController.clear(); dropdownValue1 = null; dropdownValue2 = null; }); } void clearDropDownVals() { setState(() { startDot = null; intListPath = null; dfsAccessTable = null; endDot = null; currOp = ""; op = Operations.none; }); } // *************buttons************* ElevatedButton createButton(String txt, void Function() onPressing) { return ElevatedButton( onPressed: onPressing, style: ButtonStyle( backgroundColor: MaterialStateProperty.resolveWith( (states) => Colors.green.shade700)), child: Text(txt, style: const TextStyle( fontSize: 15, color: Colors.white70, height: 1, )), ); } void showPopUp(String alertTitle, String err) => showDialog( context: context, builder: (BuildContext context) => AlertDialog( title: Text(alertTitle, style: const TextStyle(fontSize: 26)), content: Text( err, style: const TextStyle(fontSize: 18), ), actions: [ TextButton( onPressed: () => Navigator.pop(context, 'OK'), child: const Text('OK'), ), ], ), ); // *************buttons************* // ***addSpace*** SizedBox addSpaceH(double h) { return SizedBox(height: h); } SizedBox addSpaceW(double w) { return SizedBox(width: w); } // ***addSpace*** // *************inputs************* Container createInputBox(String text, double width, IconData? icon, TextEditingController? controller) { if (icon == null) { return Container( width: width, height: 40, margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), child: TextField( controller: controller, textAlign: TextAlign.center, onChanged: (name) => graphData.setName(name), decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), filled: true, fillColor: Colors.white, //prefixIcon: Icon(icon, color: Colors.black), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(40))), hintStyle: const TextStyle(color: Colors.black38), hintText: text), )); } return Container( width: width, height: 40, margin: const EdgeInsets.symmetric(horizontal: 20, vertical: 5), child: TextField( controller: controller, textAlign: TextAlign.center, decoration: InputDecoration( contentPadding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10), filled: true, fillColor: Colors.white, prefixIcon: Icon(icon, color: Colors.black), border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(40))), hintStyle: const TextStyle(color: Colors.black38), hintText: text), )); } SizedBox dropList1(double width) { var button = DropdownButton( hint: const Text( 'Select dot', style: TextStyle(color: Colors.white, fontSize: 13), ), //icon: const Icon(Icons.fiber_manual_record, color: Colors.white), alignment: AlignmentDirectional.bottomEnd, value: dropdownValue1, isDense: true, borderRadius: const BorderRadius.all(Radius.circular(20)), dropdownColor: Colors.green.shade800, style: const TextStyle( color: Colors.white, fontSize: 18, ), onChanged: (String? newValue) { setState(() { dropdownValue1 = newValue; }); }, items: graphData.getDots().map((location) { return DropdownMenuItem( child: Text(location.getName()), value: location.num.toString(), ); }).toList(), ); return SizedBox( child: button, width: width, ); } SizedBox dropList2(double width) { var button = DropdownButton( hint: const Text( 'Select Dot', style: TextStyle(color: Colors.white, fontSize: 13), ), alignment: AlignmentDirectional.centerEnd, value: dropdownValue2, isDense: true, borderRadius: const BorderRadius.all(Radius.circular(20)), dropdownColor: Colors.green.shade800, style: const TextStyle( color: Colors.white, fontSize: 18, ), onChanged: (String? newValue) { setState(() { dropdownValue2 = newValue; }); }, items: graphData.getDots().map((location) { return DropdownMenuItem( child: Text(location.getName()), value: location.num.toString(), ); }).toList(), ); return SizedBox( child: button, width: width, ); } // *************inputs************* //*********ButtonsFunctions********* void addDotPushed() { clearDropDownVals(); if (_textNameController.text == "") { showPopUp("Error", "No name in \"Dot name\" box"); } else { setState(() { String? res = graphData.addIsolated(_textNameController.text); if (res != null) { showPopUp("Error", res); } }); } clearInputData(); } void addPathPushed() { clearDropDownVals(); if (dropdownValue1 == null) { showPopUp("Error", "No dot in first box selected"); } else if (dropdownValue2 == null) { showPopUp("Error", "No dot in second box selected"); } else if (_textLnthController.text == "" && graphData.getUseLengthBool()) { showPopUp("Error", "No length in \"Input length\" box"); } else { int? from = int.parse(dropdownValue1!); int? to = int.parse(dropdownValue2!); int? len = int.tryParse(_textLnthController.text); if (len == null && graphData.getUseLengthBool()) { showPopUp("Error", "Can't parse input.\nInts only allowed in \"Input length\""); } else { len ??= 0; setState(() { String? res = graphData.addPath(from, to, len!); if (res != null) showPopUp("Error", res); }); } } clearInputData(); } void changeOriented() { setState(() { String? res = graphData.flipUseOrientation(); if (res != null) showPopUp("Error", res); }); } void changeLength() { setState(() { String? res = graphData.flipUseLength(); if (res != null) showPopUp("Error", res); }); } void delPathPushed() { clearDropDownVals(); if (dropdownValue1 == null) { showPopUp("Error", "No dot in first box selected"); } else if (dropdownValue2 == null) { showPopUp("Error", "No dot in second box selected"); } else { int? from = int.tryParse(dropdownValue1!); int? to = int.tryParse(dropdownValue2!); if (from == null || to == null) { showPopUp("Error", "Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\""); } else { setState(() { String? res = graphData.delPath(from, to); if (res != null) { showPopUp("Error", res); } }); } } clearInputData(); } void delDotPushed() { if (dropdownValue1 != null) { setState(() { String? res = graphData.delDot(int.parse(dropdownValue1!)); if (res != null) { showPopUp("Error", res); } }); } else { showPopUp("Error", "Nothing in input"); } clearDropDownVals(); clearInputData(); } void fileOpener() async { FilePickerResult? result = await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]); if (result != null) { if (!result.files.single.path!.endsWith(".txt")) { showPopUp("Error", "Can open only \".txt\" files"); } else { setState(() { String? res = graphData.replaceDataFromFile(result.files.single.path!); if (res != null) showPopUp("Error", res); }); } } else { showPopUp("Error", "No file selected"); } clearDropDownVals(); clearInputData(); } void fileSaver() async { String? outputFile = await FilePicker.platform.saveFile( dialogTitle: 'Please select an output file:', fileName: 'output-file.txt', allowedExtensions: ["txt"]); if (outputFile == null) { showPopUp("Error", "Save cancelled"); } else { if (!outputFile.endsWith(".txt")) { outputFile += ".txt"; } graphData.printToFile(outputFile); } clearDropDownVals(); clearInputData(); } void bfsPushed() { clearDropDownVals(); if (dropdownValue1 == null) { showPopUp("Error", "No dot in first box selected"); } else if (dropdownValue2 == null) { showPopUp("Error", "No dot in second box selected"); } else { setState(() { startDot = int.parse(dropdownValue1!); endDot = int.parse(dropdownValue2!); currOp = "OP: BFS from $startDot to $endDot"; op = Operations.bfs; intListPath = graphData.bfsPath(startDot!, endDot!); }); if (intListPath == null) { showPopUp("Info", "There is no path"); } //print(intListPath); } clearInputData(); } void dfsPushed() { clearDropDownVals(); if (dropdownValue1 == null) { showPopUp("Error", "No dot in first box selected"); } else { setState(() { startDot = int.parse(dropdownValue1!); op = Operations.dfs; currOp = "OP: DFS from $startDot"; dfsAccessTable = graphData.dfsIterative(startDot!); }); if (dfsAccessTable == null) { showPopUp("Err", "report this error."); } //print(dfsAccessTable); } clearInputData(); } void dijkstraPushed() { clearDropDownVals(); if (dropdownValue1 == null) { showPopUp("Error", "No number in \"Dot number\" box"); } else { setState(() { startDot = int.parse(dropdownValue1!); currOp = "OP: Dijkstra from $startDot"; op = Operations.dijkstra; intListPath = graphData.dijkstra(startDot!); }); if (intListPath == null) { showPopUp("Err", "report this error."); } //print(intListPath); } clearInputData(); } //*********ButtonsFunctions********* // build @override Widget build(BuildContext context) { screenSize = MediaQuery.of(context).size.width; _textGrNmController.text = graphData.getName(); return MaterialApp( home: Scaffold( appBar: AppBar( title: const Align( alignment: Alignment.topLeft, child: Text("Graph name:\n", style: TextStyle( fontSize: 18, color: Colors.white, ))), toolbarHeight: 110, flexibleSpace: Container( color: Colors.green.shade900, child: Column(children: [ const SizedBox(height: 5), Row(children: [ addSpaceW(screenSize / 8 + 19), createButton("\nAdd dot\n", addDotPushed), createInputBox("Dot name", screenSize / 4 - 25, Icons.label, _textNameController), addSpaceW(8), createButton("\nAdd path\n", addPathPushed), createInputBox("Input length", screenSize / 4 - 25, Icons.arrow_right_alt_outlined, _textLnthController), ]), addSpaceH(3), Row(children: [ addSpaceW(6), createInputBox( "Name", screenSize / 8 - 25, null, _textGrNmController), //addSpaceW(screenSize / 8 - 4), createButton("\nDel dot \n", delDotPushed), //createInputBox("Dot number", screenSize / 4 - 25, Icons.fiber_manual_record, _textNumbController), addSpaceW(54), dropList1(screenSize / 4 - 80), addSpaceW(53), createButton("\nDel path\n", delPathPushed), addSpaceW(54), dropList2(screenSize / 4 - 80), //createInputBox("Destination number", screenSize / 4 - 25, Icons.fiber_manual_record, _textDestController), ]), ]), ), actions: [ IconButton( onPressed: () { setState(() { clearDropDownVals(); graphData.flushData(); clearInputData(); }); }, icon: const Icon(Icons.delete_sweep), iconSize: 60, ), ]), body: CustomPaint( painter: CurvePainter( graphData: graphData, intListPath: intListPath, dfsAccessTable: dfsAccessTable, start: startDot, end: endDot, op: op), child: Align( alignment: Alignment.topRight, child: ButtonBar( mainAxisSize: MainAxisSize.min, children: [ Text( currOp, style: const TextStyle(fontSize: 14, color: Colors.black), ), createButton("Bfs", bfsPushed), createButton("Dfs", dfsPushed), createButton("Dijkstra", dijkstraPushed), createButton("Clear OP", () { clearDropDownVals(); clearInputData(); }), createButton(graphData.getUseLengthStr(), changeLength), createButton(graphData.getDoubleSidedStr(), changeOriented), createButton("Save to file", fileSaver), createButton("Load from file", fileOpener), createButton("Help", () { String out = " В поле \"Graph name\" можно сменить имя графу.\n"; out += " Для добавления точки необходимо ввести имя в \"Dot name\" и нажать на \"Add dot\".\n"; out += " Для удаления точки необходимо выбрать номер в левом выпадающем списке и нажать на \"Del dot\".\n"; out += " Для добавления пути необходимо выбрать: первую вершину в левом выпадающем списке, вторую вершину в правом выпадающем списке, "; out += "и, если граф взвешенный, то ввести длину пути в \"Input length\". Затем нажать \"Add path\".\n"; out += " Для удаления пути необходимо выбрать первую вершину из левого списка и вторую вершину в правом списке. Затем нажать \"Del path\".\n\n"; out += " Кнопка \"Bfs\" отмечает точки в пути из вершины, выбранной слева, в вершину, выбранную справа.\n"; out += " Кнопка \"Dfs\" помечает точки, которые доступны из вершины, выбранной слева."; out += " Кнопка \"Dijkstra\" ищет кратчайший путь из вершины, выбранной слева. во все другие."; out += " Кнопка \"Clear op\" отменяет \"Bfs\", \"Dfs\" и \"Dijkstra\". Все эти отображения так же отменяются при любых действиях с графом."; out += " Кнопки \"Взвешенный\" и \"Ориентированный\" позволяют сменить эти значения перед построением графа (т.е. для их работы граф должен быть пустым).\n"; out += " Кнопки \"Save to file\" и \"Load from file\" позволяют вывести информацию в файл и загрузить информацию из файла соответственно.\n"; out += " Кнопка \"Help\" описывает работу с интерфейсом программы."; showPopUp("Help:", out); }) ], ), ), ), )); } }