Graphs_dart/flutter/lib/pages/drawing_page.dart

540 lines
18 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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<Dot> d = <Dot>[];
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<StatefulWidget> createState() => _DrawingPageState();
}
class _DrawingPageState extends State<DrawingPage> {
double screenSize = 0;
Graphs graphData = getGraph();
List<int?>? intListPath;
List<bool>? dfsAccessTable;
//List<int?>? 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<Color>(
(states) => Colors.green.shade700)),
child: Text(txt,
style: const TextStyle(
fontSize: 15,
color: Colors.white70,
height: 1,
)),
);
}
void showPopUp(String alertTitle, String err) => showDialog<String>(
context: context,
builder: (BuildContext context) => AlertDialog(
title: Text(alertTitle, style: const TextStyle(fontSize: 26)),
content: Text(
err,
style: const TextStyle(fontSize: 18),
),
actions: <Widget>[
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: <Widget>[
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: <Widget>[
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);
})
],
),
),
),
));
}
}