极简浏览器启动页:打造你的个性化导航主页

核心内容摘要

GTE模型实测:中文文本相似度计算效果展示
探索Java媒体处理:Xuggle-Xuggler的技术解析与实践

vLLM-v0.11.0效果展示:实测推理速度提升5倍,支持高并发访问

效果图实现步骤

引入外部类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

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; }

小马拉大车9.1视频-小马拉大车9.1视频应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123