核心内容摘要
99综合网官方版:开启全方位进化之旅,重塑你的数字生活
效果图实现步骤
引入外部类fl_chart: ^
0.
66.
准备电压数据点和电流数据点//电压数据点 (蓝色) ListFlSpot _voltageData [ FlSpot(0,
0.
, // 00:00 FlSpot(1,
1.
, // 00:30 FlSpot(2,
2.
, // 01:00 FlSpot(3,
1.
, // 01:30 FlSpot(4,
2.
, // 02:00 FlSpot(5,
3.
, // 02:30 FlSpot(6,
2.
, // 03:00 FlSpot(7,
2.
, // 03:30 FlSpot(8,
2.
, // 04:00 FlSpot(9,
4.
, // 04:30 FlSpot(10,
4.
, // 05:00 FlSpot(11,
3.
, // 05:30 FlSpot(12,
3.
, // 06:00 FlSpot(13,
2.
, // 06:30 FlSpot(14,
6.
, // 07:00 FlSpot(15,
2.
, // 07:30 FlSpot(16,
7.
, // 08:00 FlSpot(17,
8.
, // 08:30 FlSpot(18,
8.
, // 09:00 FlSpot(19,
9.
, // 09:30 FlSpot(20,
9.
, // 10:00 FlSpot(21,
0.
, // 10:30 FlSpot(22,
9.
, // 11:00 FlSpot(23,
9.
, // 11:30 FlSpot(24,
9.
, // 12:00 FlSpot(25,
8.
, // 12:30 FlSpot(26,
8.
, // 13:00 FlSpot(27,
7.
, // 13:30 FlSpot(28,
7.
, // 14:00 FlSpot(29,
6.
, // 14:30 FlSpot(30,
6.
, // 15:00 FlSpot(31,
2.
, // 15:30 FlSpot(32,
2.
, // 16:00 FlSpot(33,
4.
, // 16:30 FlSpot(34,
4.
, // 17:00 FlSpot(35,
3.
, // 17:30 FlSpot(36,
3.
, // 18:00 FlSpot(37,
2.
, // 18:30 FlSpot(38,
2.
, // 19:00 FlSpot(39,
1.
, // 19:30 FlSpot(40,
1.
, // 20:00 FlSpot(41,
0.
, // 20:30 FlSpot(42,
0.
, // 21:00 FlSpot(43,
9.
, // 21:30 FlSpot(44,
, // 22:00 FlSpot(45,
2.
, // 22:30 FlSpot(46,
8.
, // 23:00 FlSpot(47,
2.
, // 23:30 FlSpot(48,
7.
, // 24:00 ]; //电流数据点绿色 ListFlSpot _currentData [ FlSpot(0,
, // 00:00 FlSpot(1,
, // 00:30 FlSpot(2,
, // 01:00 FlSpot(3,
, // 01:30 FlSpot(4,
, // 02:00 FlSpot(5,
, // 02:30 FlSpot(6,
, // 03:00 FlSpot(7,
, // 03:30 FlSpot(8,
, // 04:00 FlSpot(9,
, // 04:30 FlSpot(10,
, // 05:00 FlSpot(11,
, // 05:30 FlSpot(12,
, // 06:00 FlSpot(13,
, // 06:30 FlSpot(14,
, // 07:00 FlSpot(15,
, // 07:30 FlSpot(16,
, // 08:00 FlSpot(17,
, // 08:30 FlSpot(18,
, // 09:00 FlSpot(19,
, // 09:30 FlSpot(20,
, // 10:00 FlSpot(21,
, // 10:30 FlSpot(22,
, // 11:00 FlSpot(23,
, // 11:30 FlSpot(24,
, // 12:00 FlSpot(25,
, // 12:30 FlSpot(26,
, // 13:00 FlSpot(27,
, // 13:30 FlSpot(28,
, // 14:00 FlSpot(29,
, // 14:30 FlSpot(30,
, // 15:00 FlSpot(31,
, // 15:30 FlSpot(32,
, // 16:00 FlSpot(33,
, // 16:30 FlSpot(34,
, // 17:00 FlSpot(35,
, // 17:30 FlSpot(36,
, // 18:00 FlSpot(37,
, // 18:30 FlSpot(38,
, // 19:00 FlSpot(39,
, // 19:30 FlSpot(40,
, // 20:00 FlSpot(41,
, // 20:30 FlSpot(42,
, // 21:00 FlSpot(43,
, // 21:30 FlSpot(44,
, // 22:00 FlSpot(45,
, // 22:30 FlSpot(46,
, // 23:00 FlSpot(47,
, // 23:30 FlSpot(48,
, // 24:00 ];
定义一些必要的变量final ScrollController _scrollController ScrollController(); //滚动控制器 // 电压值的范围 final double _voltageMinY 0; final double _voltageMaxY 10; //电流值的范围 final double _currentMinY 0; final double _currentMaxY 1500;
销毁操作override void dispose() { _scrollController.dispose(); super.dispose(); }
构建折线图 ****
左侧Y轴
图表区域
右侧Y轴 Widget _buildFixedYAxisChart() { return Container( height: 260, child: Row( children: [ // 左侧固定Y轴 - 电压轴 Container( width: 40, padding: const EdgeInsets.only(bottom:
, color: Colors.white, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(${_voltageMaxY.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${_voltageMinY.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ], ), ), SizedBox(width:
, // 可滚动的图表区域 Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, //水平滚动 controller: _scrollController, //滚动控制器 child: Container( width: 48 *
5
0 60, height: 250, padding: const EdgeInsets.symmetric(horizontal:
20.
, child: Stack( children: [ LineChart( //折线图主体 LineChartData( lineTouchData: LineTouchData( enabled: false, //不让点击折线图显示相关点的数据 ), gridData: FlGridData( show: true, //显示网格系统 drawHorizontalLine: true, //绘制水平网格线 drawVerticalLine: true, //绘制垂直网格线 horizontalInterval:(_voltageMaxY - _voltageMinY) / 5, //水平线之间的间隔距离 verticalInterval: 1, //垂直线之间的间隔距离 - 每1个X轴单位画一条垂直线 getDrawingHorizontalLine: (value) { //水平线样式自定义 return FlLine( color: Color(0xFF
.withOpacity(
0.
, strokeWidth: 1, dashArray: [4,4] ); }, getDrawingVerticalLine: (value) { //垂直线样式自定义 return FlLine( color: Color(0xFF
.withOpacity(
0.
, strokeWidth: 1, dashArray: [4,4] ); }, ), titlesData: FlTitlesData( //坐标轴标题配置 show: true, //显示坐标轴标题 bottomTitles: AxisTitles( //底部X轴标题 sideTitles: SideTitles( showTitles: true, //显示刻度标签 reservedSize: 30, //为标签预留空间 interval: 1, //每一个单位显示一个标签 getTitlesWidget: (value, meta) { //自定义标签被内容 //判断是否为偶数 if (value.toInt() % 2
{ final hour value ~/ 2; //除2取整得到小时数 return Padding( padding: const EdgeInsets.only(top:
8.
, //顶部间距 child: Text( ${hour.toString().padLeft(2,
}:00, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ); } //判断是否为奇数 if (value.toInt() % 2
{ final hour value ~/ 2; return Padding( padding: const EdgeInsets.only(top:
8.
, child: Text( ${hour.toString().padLeft(2,
}:30, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ); } return Container(); //其他情况返回空容器 }, ), ), //上左右不显示标题 leftTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData( //边框显示 show: true, border: Border( bottom: BorderSide( //显示下边框 color: Color(0xFF
.withOpacity(
0.
, width: 1, ), top: BorderSide.none, left: BorderSide.none, right: BorderSide.none, ), ), minX: 0, //x轴最小值 maxX: 48, //X轴最大值 minY: _voltageMinY, //Y轴最小值 使用的是电压的值 maxY: _voltageMaxY, //Y轴最大值 lineBarsData: [ // 电压线 - 蓝色 LineChartBarData( spots: _voltageData, //使用预先定义的电压数据点数组 isCurved: true, //使用曲线链接点 color: Colors.blue, //线条颜色 barWidth: 2, //线条宽度 isStrokeCapRound: true, //线条端点使用圆角 dotData: const FlDotData(show: false), //不显示每个数据点的小圆点 belowBarData: BarAreaData( //渐变填充区域配置 show: true, //显示线条下方的填充区域 gradient: LinearGradient( colors: [ Colors.blue.withOpacity(
0.
, Colors.blue.withOpacity(
0.
, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), // 电流线 - 绿色需要转换到电压范围 LineChartBarData( spots: _convertCurrentToVoltageSpots(), //转为电压显示在图表上 isCurved: true, color: Colors.green, barWidth: 2, isStrokeCapRound: true, dotData: const FlDotData(show: false), belowBarData: BarAreaData( show: true, gradient: LinearGradient( colors: [ Colors.green.withOpacity(
0.
, Colors.green.withOpacity(
0.
, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), ], ), ), // 虚线装饰 Positioned( top: 0, left: 0, right: 0, child: CustomPaint( painter: _DashedLinePainter( direction: DashedLineDirection.horizontal, ), child: Container(height:
, ), ), Positioned( left: 0, top: 0, bottom: 30, child: CustomPaint( painter: _DashedLinePainter( direction: DashedLineDirection.vertical, ), child: Container(width:
, ), ), Positioned( right: 0, top: 0, bottom: 30, child: CustomPaint( painter: _DashedLinePainter( direction: DashedLineDirection.vertical, ), child: Container(width:
, ), ), ], ), ), ), ), SizedBox(width:
, // 右侧固定Y轴 - 电流轴 Container( width: 40, padding: const EdgeInsets.only(bottom:
, color: Colors.white, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(${_currentMaxY.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${_currentMinY.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ], ), ), ], ), ); }
将电流转换为电压范围的方法ListFlSpot _convertCurrentToVoltageSpots() { return _currentData.map((spot) { // 将电流值映射到电压范围 double voltageValue _voltageMinY (spot.y - _currentMinY) * ((_voltageMaxY - _voltageMinY) / (_currentMaxY - _currentMinY)); return FlSpot(spot.x, voltageValue); }).toList(); }
UI架构override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( onPressed: () { Navigator.pop(context); }, icon: Icon(Icons.arrow_back_ios), ), title: const Text(电流电压监测), centerTitle: true, elevation: 0, ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 图表标题 Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16,
, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 单位V, style: const TextStyle( fontSize: 12, color: Color(0xFF09172F) ), ), Text( 单位mA, style: const TextStyle( fontSize: 12, color: Color(0xFF09172F) ), ), ], ), ), // 固定Y轴的可滚动图表 Container( margin: const EdgeInsets.symmetric(horizontal:
, decoration: BoxDecoration( color: Colors.white, ), child: Column( children: [ _buildFixedYAxisChart(), ], ), ), // 操作按钮 Padding( padding: const EdgeInsets.all(
, child: Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: _scrollToNow, icon: const Icon(Icons.access_time, size:
, label: const Text(跳转到当前), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical:
, ), ), ), ], ), ), ], ), ), ); }
跳转到当前时间的方法void _scrollToNow() { final now DateTime.now(); final hour now.hour; final minute now.minute; int index hour * 2; if (minute
{ index 1; } index index.clamp(0,
; final scrollPosition index *
5
0 - MediaQuery.of(context).size.width / 2 100; _scrollController.animateTo( scrollPosition.clamp(0, _scrollController.position.maxScrollExtent), duration: const Duration(milliseconds:
, curve: Curves.easeInOut, ); ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(已跳转到 ${hour.toString().padLeft(2,
}:${minute.toString().padLeft(2,
}), duration: const Duration(seconds:
, ), ); }
虚线类enum DashedLineDirection { horizontal, vertical } class _DashedLinePainter extends CustomPainter { final DashedLineDirection direction; final Color color; final double strokeWidth; final double dashLength; final double dashSpace; final double opacity; _DashedLinePainter({ this.direction DashedLineDirection.horizontal, this.color const Color(0xFF
, this.strokeWidth
0, this.dashLength
0, this.dashSpace
0, this.opacity
4, }); override void paint(Canvas canvas, Size size) { final paint Paint() ..color color.withOpacity(opacity) ..strokeWidth strokeWidth ..style PaintingStyle.stroke; if (direction DashedLineDirection.horizontal) { double startX 0; while (startX size.width) { canvas.drawLine( Offset(startX,
, Offset(startX dashLength,
, paint, ); startX dashLength dashSpace; } } else { double startY 0; while (startY size.height) { canvas.drawLine( Offset(0, startY), Offset(0, startY dashLength), paint, ); startY dashLength dashSpace; } } } override bool shouldRepaint(covariant CustomPainter oldDelegate) false; }
代码实例import package:flutter/cupertino.dart; import package:flutter/material.dart; import package:fl_chart/fl_chart.dart; import dart:math; class CurrentVoltageCard extends StatefulWidget { const CurrentVoltageCard({super.key}); override StateStatefulWidget createState() _CurrentVoltageCardState(); } class _CurrentVoltageCardState extends StateCurrentVoltageCard { //电压数据点 (蓝色) ListFlSpot _voltageData [ FlSpot(0,
0.
, // 00:00 FlSpot(1,
1.
, // 00:30 FlSpot(2,
2.
, // 01:00 FlSpot(3,
1.
, // 01:30 FlSpot(4,
2.
, // 02:00 FlSpot(5,
3.
, // 02:30 FlSpot(6,
2.
, // 03:00 FlSpot(7,
2.
, // 03:30 FlSpot(8,
2.
, // 04:00 FlSpot(9,
4.
, // 04:30 FlSpot(10,
4.
, // 05:00 FlSpot(11,
3.
, // 05:30 FlSpot(12,
3.
, // 06:00 FlSpot(13,
2.
, // 06:30 FlSpot(14,
6.
, // 07:00 FlSpot(15,
2.
, // 07:30 FlSpot(16,
7.
, // 08:00 FlSpot(17,
8.
, // 08:30 FlSpot(18,
8.
, // 09:00 FlSpot(19,
9.
, // 09:30 FlSpot(20,
9.
, // 10:00 FlSpot(21,
0.
, // 10:30 FlSpot(22,
9.
, // 11:00 FlSpot(23,
9.
, // 11:30 FlSpot(24,
9.
, // 12:00 FlSpot(25,
8.
, // 12:30 FlSpot(26,
8.
, // 13:00 FlSpot(27,
7.
, // 13:30 FlSpot(28,
7.
, // 14:00 FlSpot(29,
6.
, // 14:30 FlSpot(30,
6.
, // 15:00 FlSpot(31,
2.
, // 15:30 FlSpot(32,
2.
, // 16:00 FlSpot(33,
4.
, // 16:30 FlSpot(34,
4.
, // 17:00 FlSpot(35,
3.
, // 17:30 FlSpot(36,
3.
, // 18:00 FlSpot(37,
2.
, // 18:30 FlSpot(38,
2.
, // 19:00 FlSpot(39,
1.
, // 19:30 FlSpot(40,
1.
, // 20:00 FlSpot(41,
0.
, // 20:30 FlSpot(42,
0.
, // 21:00 FlSpot(43,
9.
, // 21:30 FlSpot(44,
, // 22:00 FlSpot(45,
2.
, // 22:30 FlSpot(46,
8.
, // 23:00 FlSpot(47,
2.
, // 23:30 FlSpot(48,
7.
, // 24:00 ]; //电流数据点绿色 ListFlSpot _currentData [ FlSpot(0,
, // 00:00 FlSpot(1,
, // 00:30 FlSpot(2,
, // 01:00 FlSpot(3,
, // 01:30 FlSpot(4,
, // 02:00 FlSpot(5,
, // 02:30 FlSpot(6,
, // 03:00 FlSpot(7,
, // 03:30 FlSpot(8,
, // 04:00 FlSpot(9,
, // 04:30 FlSpot(10,
, // 05:00 FlSpot(11,
, // 05:30 FlSpot(12,
, // 06:00 FlSpot(13,
, // 06:30 FlSpot(14,
, // 07:00 FlSpot(15,
, // 07:30 FlSpot(16,
, // 08:00 FlSpot(17,
, // 08:30 FlSpot(18,
, // 09:00 FlSpot(19,
, // 09:30 FlSpot(20,
, // 10:00 FlSpot(21,
, // 10:30 FlSpot(22,
, // 11:00 FlSpot(23,
, // 11:30 FlSpot(24,
, // 12:00 FlSpot(25,
, // 12:30 FlSpot(26,
, // 13:00 FlSpot(27,
, // 13:30 FlSpot(28,
, // 14:00 FlSpot(29,
, // 14:30 FlSpot(30,
, // 15:00 FlSpot(31,
, // 15:30 FlSpot(32,
, // 16:00 FlSpot(33,
, // 16:30 FlSpot(34,
, // 17:00 FlSpot(35,
, // 17:30 FlSpot(36,
, // 18:00 FlSpot(37,
, // 18:30 FlSpot(38,
, // 19:00 FlSpot(39,
, // 19:30 FlSpot(40,
, // 20:00 FlSpot(41,
, // 20:30 FlSpot(42,
, // 21:00 FlSpot(43,
, // 21:30 FlSpot(44,
, // 22:00 FlSpot(45,
, // 22:30 FlSpot(46,
, // 23:00 FlSpot(47,
, // 23:30 FlSpot(48,
, // 24:00 ]; final ScrollController _scrollController ScrollController(); //滚动控制器 // 电压值的范围 final double _voltageMinY 0; final double _voltageMaxY 10; //电流值的范围 final double _currentMinY 0; final double _currentMaxY 1500; override void dispose() { _scrollController.dispose(); super.dispose(); } // 构建折线图 Widget _buildFixedYAxisChart() { return Container( height: 260, child: Row( children: [ // 左侧固定Y轴 - 电压轴 Container( width: 40, padding: const EdgeInsets.only(bottom:
, color: Colors.white, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.end, children: [ Text(${_voltageMaxY.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_voltageMinY (_voltageMaxY - _voltageMinY) *
0.
.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${_voltageMinY.toStringAsFixed(
}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ], ), ), SizedBox(width:
, // 可滚动的图表区域 Expanded( child: SingleChildScrollView( scrollDirection: Axis.horizontal, //水平滚动 controller: _scrollController, //滚动控制器 child: Container( width: 48 *
5
0 60, height: 250, padding: const EdgeInsets.symmetric(horizontal:
20.
, child: Stack( children: [ LineChart( //折线图主体 LineChartData( lineTouchData: LineTouchData( enabled: false, //不让点击折线图显示相关点的数据 ), gridData: FlGridData( show: true, //显示网格系统 drawHorizontalLine: true, //绘制水平网格线 drawVerticalLine: true, //绘制垂直网格线 horizontalInterval:(_voltageMaxY - _voltageMinY) / 5, //水平线之间的间隔距离 verticalInterval: 1, //垂直线之间的间隔距离 - 每1个X轴单位画一条垂直线 getDrawingHorizontalLine: (value) { //水平线样式自定义 return FlLine( color: Color(0xFF
.withOpacity(
0.
, strokeWidth: 1, dashArray: [4,4] ); }, getDrawingVerticalLine: (value) { //垂直线样式自定义 return FlLine( color: Color(0xFF
.withOpacity(
0.
, strokeWidth: 1, dashArray: [4,4] ); }, ), titlesData: FlTitlesData( //坐标轴标题配置 show: true, //显示坐标轴标题 bottomTitles: AxisTitles( //底部X轴标题 sideTitles: SideTitles( showTitles: true, //显示刻度标签 reservedSize: 30, //为标签预留空间 interval: 1, //每一个单位显示一个标签 getTitlesWidget: (value, meta) { //自定义标签被内容 //判断是否为偶数 if (value.toInt() % 2
{ final hour value ~/ 2; //除2取整得到小时数 return Padding( padding: const EdgeInsets.only(top:
8.
, //顶部间距 child: Text( ${hour.toString().padLeft(2,
}:00, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ); } //判断是否为奇数 if (value.toInt() % 2
{ final hour value ~/ 2; return Padding( padding: const EdgeInsets.only(top:
8.
, child: Text( ${hour.toString().padLeft(2,
}:30, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ); } return Container(); //其他情况返回空容器 }, ), ), //上左右不显示标题 leftTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), rightTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), topTitles: const AxisTitles( sideTitles: SideTitles(showTitles: false), ), ), borderData: FlBorderData( //边框显示 show: true, border: Border( bottom: BorderSide( //显示下边框 color: Color(0xFF
.withOpacity(
0.
, width: 1, ), top: BorderSide.none, left: BorderSide.none, right: BorderSide.none, ), ), minX: 0, //x轴最小值 maxX: 48, //X轴最大值 minY: _voltageMinY, //Y轴最小值 使用的是电压的值 maxY: _voltageMaxY, //Y轴最大值 lineBarsData: [ // 电压线 - 蓝色 LineChartBarData( spots: _voltageData, //使用预先定义的电压数据点数组 isCurved: true, //使用曲线链接点 color: Colors.blue, //线条颜色 barWidth: 2, //线条宽度 isStrokeCapRound: true, //线条端点使用圆角 dotData: const FlDotData(show: false), //不显示每个数据点的小圆点 belowBarData: BarAreaData( //渐变填充区域配置 show: true, //显示线条下方的填充区域 gradient: LinearGradient( colors: [ Colors.blue.withOpacity(
0.
, Colors.blue.withOpacity(
0.
, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), // 电流线 - 绿色需要转换到电压范围 LineChartBarData( spots: _convertCurrentToVoltageSpots(), //转为电压显示在图表上 isCurved: true, color: Colors.green, barWidth: 2, isStrokeCapRound: true, dotData: const FlDotData(show: false), belowBarData: BarAreaData( show: true, gradient: LinearGradient( colors: [ Colors.green.withOpacity(
0.
, Colors.green.withOpacity(
0.
, ], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), ), ], ), ), // 虚线装饰 Positioned( top: 0, left: 0, right: 0, child: CustomPaint( painter: _DashedLinePainter( direction: DashedLineDirection.horizontal, ), child: Container(height:
, ), ), Positioned( left: 0, top: 0, bottom: 30, child: CustomPaint( painter: _DashedLinePainter( direction: DashedLineDirection.vertical, ), child: Container(width:
, ), ), Positioned( right: 0, top: 0, bottom: 30, child: CustomPaint( painter: _DashedLinePainter( direction: DashedLineDirection.vertical, ), child: Container(width:
, ), ), ], ), ), ), ), SizedBox(width:
, // 右侧固定Y轴 - 电流轴 Container( width: 40, padding: const EdgeInsets.only(bottom:
, color: Colors.white, child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(${_currentMaxY.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${(_currentMinY (_currentMaxY - _currentMinY) *
0.
.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), Text(${_currentMinY.toInt()}, style: const TextStyle( fontSize: 10, color: Colors.grey, ), ), ], ), ), ], ), ); } //将电流值转换到电压范围 ListFlSpot _convertCurrentToVoltageSpots() { return _currentData.map((spot) { // 将电流值映射到电压范围 double voltageValue _voltageMinY (spot.y - _currentMinY) * ((_voltageMaxY - _voltageMinY) / (_currentMaxY - _currentMinY)); return FlSpot(spot.x, voltageValue); }).toList(); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( leading: IconButton( onPressed: () { Navigator.pop(context); }, icon: Icon(Icons.arrow_back_ios), ), title: const Text(电流电压监测), centerTitle: true, elevation: 0, ), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 图表标题 Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16,
, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 单位V, style: const TextStyle( fontSize: 12, color: Color(0xFF09172F) ), ), Text( 单位mA, style: const TextStyle( fontSize: 12, color: Color(0xFF09172F) ), ), ], ), ), // 固定Y轴的可滚动图表 Container( margin: const EdgeInsets.symmetric(horizontal:
, decoration: BoxDecoration( color: Colors.white, ), child: Column( children: [ _buildFixedYAxisChart(), ], ), ), // 操作按钮 Padding( padding: const EdgeInsets.all(
, child: Row( children: [ Expanded( child: ElevatedButton.icon( onPressed: _scrollToNow, icon: const Icon(Icons.access_time, size:
, label: const Text(跳转到当前), style: ElevatedButton.styleFrom( padding: const EdgeInsets.symmetric(vertical:
, ), ), ), ], ), ), ], ), ), ); } // 跳转到当前时间 void _scrollToNow() { final now DateTime.now(); final hour now.hour; final minute now.minute; int index hour * 2; if (minute
{ index 1; } index index.clamp(0,
; final scrollPosition index *
5