pile_nav_new/lib/pages/real/component/chart.dart

364 lines
10 KiB
Dart
Raw Normal View History

2024-08-29 17:45:39 +08:00
import 'dart:math';
2024-11-06 17:23:29 +08:00
import 'dart:developer' as dev;
2024-08-29 17:45:39 +08:00
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:intl/intl.dart';
import '../../../service/base.dart';
import '../process.dart';
class ProcessChart extends StatefulWidget {
final int pileId;
const ProcessChart({super.key, required this.pileId});
@override
State<ProcessChart> createState() => _ProcessChartState();
}
class _ProcessChartState extends State<ProcessChart> {
List<ProcessEntity> processList = [];
List<Widget> chartTitleWidget = [];
@override
void initState() {
super.initState();
fetchData();
}
int maxX = 5;
int maxYL = 14; //深度
int maxYR = 0; //10cm
ChartData chartData = ChartData([]);
void fetchData() async {
try {
SchedulerBinding.instance.addPostFrameCallback((_) async {
//获取点的数据
List detailCdate = await GetServices().getProcessData(widget.pileId);
setState(() {
double ten1Max = 0;
double ten2Max = 0;
for (var i = 0; i < detailCdate.length; i++) {
ProcessEntity process = ProcessEntity.fromJson(detailCdate[i]);
processList.add(process);
// 左(深度)右(10cm流量)标题栏最大值计算
maxYL = max(maxYL, process.depth.ceil());
ten1Max = max(ten1Max, process.subtotalFlow1);
ten2Max = max(ten2Max, process.subtotalFlow2);
maxYR = max(ten2Max.ceil(), ten1Max.ceil());
}
int radtio = (maxYR / maxYL).ceil();
chartData = ChartData(processList, maxX, maxYL, maxYR, radtio);
chartTitleWidget = chartData.chartTitleWeidget();
});
});
} catch (e) {
2024-11-06 17:23:29 +08:00
dev.log("错误");
2024-08-29 17:45:39 +08:00
setState(() {
processList = [];
});
}
}
@override
void dispose() {
super.dispose();
SystemChrome.setPreferredOrientations([]);
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
Positioned(
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: chartTitleWidget)),
LineChart(chartData.lineChart)
],
);
}
}
class ChartTileModel {
String title;
Color colors;
ChartTileModel(
this.title,
this.colors,
);
}
class ChartData {
List<ProcessEntity> processList;
int maxX;
// 这3个值变化
int maxYL;
int maxYR;
int radtio;
List<ChartTileModel> chartTitle = [
ChartTileModel("深度:", Colors.green),
ChartTileModel("10cm流量1", Colors.pink),
ChartTileModel("10cm流量2", Colors.cyan),
];
ChartData(this.processList,
[this.maxX = 5, this.maxYL = 14, this.maxYR = 100, this.radtio = 8]);
List<Widget> chartTitleWeidget() {
List<Widget> chartTitleWidget = [];
for (var i = 0; i < chartTitle.length; i++) {
ChartTileModel tileModel = chartTitle[i];
List<Widget> item = [
Text(tileModel.title),
Container(
width: 13,
height: 8,
margin: const EdgeInsets.only(right: 5),
decoration: BoxDecoration(color: tileModel.colors),
),
const SizedBox(
width: 10,
height: 10,
),
];
chartTitleWidget.addAll(item);
}
return chartTitleWidget;
}
LineChartData get lineChart => LineChartData(
lineTouchData: lineTouchData,
gridData: gridData,
titlesData: titlesData,
borderData: borderData,
lineBarsData: lineBarsData,
minX: 0,
maxX: maxX.toDouble(),
maxY: maxYR.toDouble(),
minY: 0,
);
LineTouchData get lineTouchData => LineTouchData(
handleBuiltInTouches: true,
touchTooltipData: LineTouchTooltipData(
fitInsideHorizontally: true,
fitInsideVertically: true,
// tooltipBgColor: Colors.black38.withOpacity(0.8),
getTooltipItems: (List<LineBarSpot> touchedSpots) {
// 自定义提示框内容
return touchedSpots.map((LineBarSpot touchedSpot) {
// 找到对应的数据点的索引
final x = touchedSpot.x;
int index = (x / maxX * processList.length).round();
ProcessEntity process = processList[index];
String lineToolValue = "";
TextStyle lineToolStyle = const TextStyle(
color: Colors.green,
);
if (touchedSpot.barIndex == 0) {
lineToolValue =
"时间:${DateFormat('HH:mm:ss').format(DateTime.parse(process.recvTime)).toString()}\n深度:${process.depth}\n累计流量:${process.toatalFlow1}";
lineToolStyle = const TextStyle(color: Colors.green);
} else if (touchedSpot.barIndex == 1) {
lineToolValue = "10cm流量1:${process.subtotalFlow1}";
lineToolStyle = const TextStyle(color: Colors.pink);
} else if (touchedSpot.barIndex == 2) {
lineToolValue = "10cm流量2:${process.subtotalFlow2}";
lineToolStyle = const TextStyle(color: Colors.cyan);
}
return LineTooltipItem(lineToolValue, lineToolStyle,
textAlign: TextAlign.left);
}).toList();
},
),
);
// FlGridData get gridData => const FlGridData(show: false);
//网格
FlGridData get gridData => const FlGridData(drawHorizontalLine: true);
//设置标题
FlTitlesData get titlesData => FlTitlesData(
bottomTitles: AxisTitles(
sideTitles: bottomTitles,
),
rightTitles: AxisTitles(
sideTitles: rightTitles(),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: leftTitles(),
),
);
SideTitles get bottomTitles => SideTitles(
showTitles: true,
reservedSize: 32,
interval: 1,
getTitlesWidget: bottomTitleWidgets,
);
SideTitles leftTitles() => SideTitles(
getTitlesWidget: leftTitleWidgets,
showTitles: true,
interval: 1,
reservedSize: 40,
);
SideTitles rightTitles() => SideTitles(
getTitlesWidget: rightTitleWidgets,
showTitles: true,
interval: 1,
reservedSize: 40,
);
//x轴
Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
);
String getTimeFromIndex(int index) {
if (processList.isEmpty || index < 0 || index >= processList.length) {
return '';
}
return processList[index].recvTime.toString().split(" ")[1];
}
int index;
switch (value.toInt()) {
case 0:
index = 0;
break;
case 1:
index = (processList.length / 5).round();
break;
case 2:
index = (processList.length * 2 / 5).round();
break;
case 3:
index = (processList.length * 3 / 5).round();
break;
case 4:
index = (processList.length * 4 / 5).round();
break;
case 5:
index = processList.length - 1;
break;
default:
return SideTitleWidget(
axisSide: meta.axisSide,
space: 10,
child: const Text(''),
);
}
return SideTitleWidget(
axisSide: meta.axisSide,
space: 10,
child: Text(
getTimeFromIndex(index),
style: style,
),
);
}
final style = const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 14,
);
//左y轴
Widget leftTitleWidgets(double value, TitleMeta meta) {
String text;
int intValue = (value).ceil();
if (intValue % (2 * radtio) == 0 && intValue <= maxYR) {
text = (intValue / radtio).floor().toString();
if (maxYR - intValue < 2 * radtio) {
text = "深度(m)";
}
} else {
return const Text("");
}
return Text(text, style: style, textAlign: TextAlign.center);
}
//右y轴
Widget rightTitleWidgets(double value, TitleMeta meta) {
String text;
int intValue = value.toInt();
if (intValue >= 0 && intValue % (2 * radtio) == 0 && intValue <= maxYR) {
text = (intValue.ceil()).toString();
if (maxYR - intValue < 2 * radtio) {
text = "流量(L)";
}
} else {
return Container();
}
return Text(text, style: style, textAlign: TextAlign.center);
}
//边界数据
FlBorderData get borderData => FlBorderData(
show: true,
border: const Border(
bottom: BorderSide(color: Colors.grey, width: 4),
left: BorderSide(color: Colors.transparent),
right: BorderSide(color: Colors.transparent),
top: BorderSide(color: Colors.transparent),
),
);
//数据
List<LineChartBarData> get lineBarsData => [
lineChartBarDataTen1,
lineChartBarDataTen2,
lineChartBarDataDepth,
];
LineChartBarData get lineChartBarDataDepth => LineChartBarData(
isCurved: false,
color: Colors.green,
barWidth: 3,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: processList.map((item) {
int index = processList.indexOf(item);
return FlSpot(
(maxX / processList.length) * index, (item.depth * radtio));
}).toList());
LineChartBarData get lineChartBarDataTen1 => LineChartBarData(
isCurved: false,
color: Colors.pink,
barWidth: 3,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: false,
color: Colors.pink,
),
spots: processList.map((item) {
int index = processList.indexOf(item);
return FlSpot((maxX / processList.length) * index, item.subtotalFlow1);
}).toList());
LineChartBarData get lineChartBarDataTen2 => LineChartBarData(
isCurved: false,
color: Colors.cyan,
barWidth: 3,
isStrokeCapRound: true,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(show: false),
spots: processList.map((item) {
int index = processList.indexOf(item);
return FlSpot((maxX / processList.length) * index, item.subtotalFlow2);
}).toList());
}