Graphs_dart/flutter/lib/pages/drawing_page.dart

417 lines
15 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/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*********
}