import 'dart:math'; import 'dart:developer' as dev; 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 createState() => _ProcessChartState(); } class _ProcessChartState extends State { List processList = []; List 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) { dev.log("错误"); 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 processList; int maxX; // 这3个值变化 int maxYL; int maxYR; int radtio; List 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 chartTitleWeidget() { List chartTitleWidget = []; for (var i = 0; i < chartTitle.length; i++) { ChartTileModel tileModel = chartTitle[i]; List 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 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 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()); }