417 lines
15 KiB
Dart
417 lines
15 KiB
Dart
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<Dot> d = <Dot>[];
|
||
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<StatefulWidget> createState() => _DrawingPageState();
|
||
}
|
||
|
||
class _DrawingPageState extends State<DrawingPage> {
|
||
double screenSize = 0;
|
||
Graphs graphData = getGraph();
|
||
List<int>? bfsPath;
|
||
List<bool>? dfsAccessTable;
|
||
int? dfsStartDot;
|
||
|
||
final _textNameController = TextEditingController();
|
||
final _textNumbController = TextEditingController();
|
||
final _textDestController = TextEditingController();
|
||
final _textLnthController = TextEditingController();
|
||
final _textGrNmController = TextEditingController();
|
||
|
||
void clearTextControllers() {
|
||
_textDestController.clear();
|
||
_textNumbController.clear();
|
||
_textLnthController.clear();
|
||
_textNameController.clear();
|
||
}
|
||
|
||
@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(5),
|
||
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(13),
|
||
createButton("\nDel path\n", delPathPushed),
|
||
createInputBox("Destination number", screenSize / 4 - 25,
|
||
Icons.fiber_manual_record, _textDestController),
|
||
]),
|
||
]),
|
||
),
|
||
actions: [
|
||
IconButton(
|
||
onPressed: () => graphData.flushData(),
|
||
icon: const Icon(Icons.delete_sweep),
|
||
iconSize: 60,
|
||
),
|
||
]),
|
||
body: CustomPaint(
|
||
painter: CurvePainter(
|
||
graphData: graphData,
|
||
bfsPath: bfsPath,
|
||
dfsStart: dfsStartDot,
|
||
dfsAccessTable: dfsAccessTable),
|
||
child: Align(
|
||
alignment: Alignment.topRight,
|
||
child: ButtonBar(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: <Widget>[
|
||
createButton("Bfs", bfsPushed),
|
||
createButton("Dfs", dfsPushed),
|
||
createButton("Clear dfs or bfs", () => bfsPath = null),
|
||
createButton(graphData.getUseLengthStr(), changeLength),
|
||
createButton(graphData.getDoubleSidedStr(), changeOriented),
|
||
/*Text(_textGrNmController.text,
|
||
style:
|
||
TextStyle(fontSize: 15, color: Colors.blueGrey.shade900)),*/
|
||
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);
|
||
})
|
||
],
|
||
),
|
||
),
|
||
),
|
||
));
|
||
}
|
||
|
||
// ignore: avoid_types_as_parameter_names, non_constant_identifier_names, use_function_type_syntax_for_parameters
|
||
ElevatedButton createButton(String txt, void 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,
|
||
)),
|
||
);
|
||
}
|
||
|
||
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 addSpaceH(double h) {
|
||
return SizedBox(height: h);
|
||
}
|
||
|
||
SizedBox addSpaceW(double w) {
|
||
return SizedBox(width: w);
|
||
}
|
||
|
||
void showPopUp(String alertTitle, String err) => showDialog<String>(
|
||
context: context,
|
||
builder: (BuildContext context) => AlertDialog(
|
||
title: Text(alertTitle),
|
||
content: Text(err),
|
||
actions: <Widget>[
|
||
TextButton(
|
||
onPressed: () => Navigator.pop(context, 'OK'),
|
||
child: const Text('OK'),
|
||
),
|
||
],
|
||
),
|
||
);
|
||
|
||
//*********ButtonsFunctions*********
|
||
void addDotPushed() {
|
||
//showPopUp("Test", "Test message");
|
||
//var inp = int.tryParse(_textNameController.text);
|
||
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);
|
||
}
|
||
}
|
||
clearTextControllers();
|
||
});
|
||
}
|
||
|
||
void addPathPushed() {
|
||
setState(() {
|
||
if (_textNumbController.text == "") {
|
||
showPopUp("Error", "No number in \"Dot number\" box");
|
||
} else if (_textDestController.text == "") {
|
||
showPopUp("Error", "No number in \"Destination number\" box");
|
||
} else if (_textLnthController.text == "" &&
|
||
graphData.getUseLengthBool()) {
|
||
showPopUp("Error", "No length in \"Input length\" box");
|
||
} else {
|
||
int? from = int.tryParse(_textNumbController.text);
|
||
int? to = int.tryParse(_textDestController.text);
|
||
int? len = int.tryParse(_textLnthController.text);
|
||
if (from == null ||
|
||
to == null ||
|
||
(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);
|
||
}
|
||
}
|
||
}
|
||
clearTextControllers();
|
||
});
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
}
|
||
clearTextControllers();
|
||
});
|
||
}
|
||
|
||
void delDotPushed() {
|
||
setState(() {
|
||
if (_textNumbController.text == "") {
|
||
showPopUp("Error", "No number in \"Dot number\" box");
|
||
} else {
|
||
int? dot = int.tryParse(_textNumbController.text);
|
||
if (dot == null) {
|
||
showPopUp("Error", "Can't parse input.\nInts only allowed");
|
||
} else {
|
||
String? res = graphData.delDot(dot);
|
||
if (res != null) {
|
||
showPopUp("Error", res);
|
||
}
|
||
}
|
||
}
|
||
clearTextControllers();
|
||
});
|
||
}
|
||
|
||
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 {
|
||
//print(result.files.single.path!);
|
||
String? res =
|
||
graphData.replaceDataFromFile(result.files.single.path!);
|
||
if (res != null) showPopUp("Error", res);
|
||
}
|
||
} else {
|
||
showPopUp("Error", "No file selected");
|
||
// User canceled the picker
|
||
}
|
||
});
|
||
}
|
||
|
||
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");
|
||
// User canceled the picker
|
||
} else {
|
||
if (!outputFile.endsWith(".txt")) {
|
||
outputFile += ".txt";
|
||
}
|
||
graphData.printToFile(outputFile);
|
||
}
|
||
}
|
||
|
||
void bfsPushed() {
|
||
setState(() {
|
||
dfsAccessTable = null;
|
||
bfsPath = null;
|
||
if (_textNumbController.text == "") {
|
||
showPopUp("Error", "No number in \"Dot number\" box");
|
||
} else if (_textDestController.text == "") {
|
||
showPopUp("Error", "No number in \"Destination number\" 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 {
|
||
bfsPath = graphData.bfsPath(from, to);
|
||
if (bfsPath == null) {
|
||
showPopUp("Info", "There is no path");
|
||
}
|
||
print(bfsPath);
|
||
}
|
||
}
|
||
clearTextControllers();
|
||
});
|
||
}
|
||
|
||
void dfsPushed() {
|
||
setState(() {
|
||
bfsPath = null;
|
||
bfsPath = null;
|
||
if (_textNumbController.text == "") {
|
||
showPopUp("Error", "No number in \"Dot number\" box");
|
||
} else {
|
||
int? from = int.tryParse(_textNumbController.text);
|
||
if (from == null) {
|
||
showPopUp("Error",
|
||
"Can't parse input.\nInts only allowed in \"Dot number\".");
|
||
} else {
|
||
dfsAccessTable = graphData.dfsIterative(from);
|
||
if (dfsAccessTable == null) {
|
||
showPopUp("Err", "report this error.");
|
||
}
|
||
print(dfsAccessTable);
|
||
}
|
||
}
|
||
clearTextControllers();
|
||
});
|
||
}
|
||
//*********ButtonsFunctions*********
|
||
}
|