import 'package:graphs/src/graph.dart'; import 'package:graphs/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], [5, 1])); d.add(Dot.fromTwoLists("2", [1, 3], [1, 1])); d.add(Dot.fromTwoLists("3", [1, 2], [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? bfsPath; List? dfsAccessTable; int? startDot; int? endDot; String? dropdownValue1; String? dropdownValue2; final _textNameController = TextEditingController(); final _textNumbController = TextEditingController(); final _textDestController = TextEditingController(); final _textLnthController = TextEditingController(); final _textGrNmController = TextEditingController(); void clearInputData() { setState(() { _textDestController.clear(); _textNumbController.clear(); _textLnthController.clear(); _textNameController.clear(); dropdownValue1 = null; dropdownValue2 = null; /*startDot = null; bfsPath = null; dfsAccessTable = null; endDot = null;*/ }); } // *************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), content: Text(err), 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 wid, IconData? icon, TextEditingController? controller) { if (icon == null) { return Container( width: wid, 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: wid, 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() { setState(() { if (_textNameController.text == "") { showPopUp("Error", "No name in \"Dot name\" box"); } else { String? res = graphData.addIsolated(_textNameController.text); if (res != null) { showPopUp("Error", res); } } clearInputData(); }); } void addPathPushed() { setState(() { if (dropdownValue1 == null) { showPopUp("Error", "Select output dot"); } else if (dropdownValue2 == null) { showPopUp("Error", "select input dot"); } 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 \"Dot number\", \"Destination number\" and \"Input length\""); } else { len ??= 0; 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() { setState(() { if (_textNumbController.text == "") { showPopUp("Error", "No number in \"Dot number\" box"); } else if (_textDestController.text == "") { showPopUp("Error", "No name in \"Dot name\" box"); } else { int? from = int.tryParse(_textNumbController.text); int? to = int.tryParse(_textDestController.text); if (from == null || to == null) { showPopUp("Error", "Can't parse input.\nInts only allowed in \"Dot number\" and \"Destination number\""); } else { String? res = graphData.delPath(from, to); if (res != null) { showPopUp("Error", res); } } } clearInputData(); }); } void delDotPushed() { setState(() { if (dropdownValue1 != null) { graphData.delDot(int.parse(dropdownValue1!)); } else { showPopUp("Error", "Nothing in input"); } clearInputData(); }); } void fileOpener() async { FilePickerResult? result = await FilePicker.platform.pickFiles(allowedExtensions: ["txt"]); setState(() { if (result != null) { if (!result.files.single.path!.endsWith(".txt")) { showPopUp("Error", "Can open only \".txt\" files"); } else { String? res = graphData.replaceDataFromFile(result.files.single.path!); if (res != null) showPopUp("Error", res); } } else { showPopUp("Error", "No file selected"); } }); } 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); } } void bfsPushed() { setState(() { bfsPath = null; dfsAccessTable = null; startDot = null; endDot = null; if (dropdownValue1 == null) { showPopUp("Error", "No number in \"Dot number\" box"); } else if (dropdownValue2 == null) { showPopUp("Error", "No number in \"Destination number\" box"); } else { startDot = int.parse(dropdownValue1!); endDot = int.parse(dropdownValue2!); bfsPath = graphData.bfsPath(startDot!, endDot!); if (bfsPath == null) { showPopUp("Info", "There is no path"); } print(bfsPath); } }); clearInputData(); } void dfsPushed() { setState(() { bfsPath = null; dfsAccessTable = null; startDot = null; endDot = null; if (dropdownValue1 == null) { showPopUp("Error", "No number in \"Dot number\" box"); } else { startDot = int.parse(dropdownValue1!); dfsAccessTable = graphData.dfsIterative(startDot!); if (dfsAccessTable == null) { showPopUp("Err", "report this error."); } print(dfsAccessTable); } 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(54), 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(() { startDot = null; endDot = null; bfsPath = null; dfsAccessTable = null; graphData.flushData(); clearInputData(); }); }, icon: const Icon(Icons.delete_sweep), iconSize: 60, ), ]), body: CustomPaint( painter: CurvePainter( graphData: graphData, bfsPath: bfsPath, dfsAccessTable: dfsAccessTable, start: startDot, end: endDot), child: Align( alignment: Alignment.topRight, child: ButtonBar( mainAxisSize: MainAxisSize.min, children: [ createButton("Bfs", bfsPushed), createButton("Dfs", dfsPushed), createButton("Clear dfs or bfs", () { setState(() { bfsPath = null; dfsAccessTable = null; startDot = null; endDot = null; 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 += " Для удаления точки необходимо ввести номер в \"Dot number\" и нажать на \"Del dot\".\n"; out += " Для добавления пути необходимо ввести: номер выходной вершины в \"Dot number\", номер входной вершины в \"Destination number\" "; out += "и, если граф взвешенный, то ввести длину пути в \"Input length\". Затем нажать \"Add path\".\n"; out += " Для удаления пути необходимо ввести номер выходной вершины в \"Dot number\" и номер входной вершины в \"Destination number\". Затем нажать \"Del path\".\n\n"; out += " Кнопки \"Bfs\" и \"Dfs\" нумеруют точки в зависимости от послежовательности, в которой они будут пройдены.\n"; out += " Кнопки \"Взвешенный\" и \"Ориентированный\" позволяют сменить эти значения перед построением графа (т.е. для их работы граф должен быть пустым).\n"; out += " Кнопки \"Save to file\" и \"Load from file\" позволяют вывести информацию в файл и загрузить информацию из файла соответственно.\n"; out += " Кнопка \"Help\" описывает работу с интерфейсом программы."; showPopUp("Help:", out); }) ], ), ), ), )); } }