flutter 曲线的使用 实现左右滑动
TemperatureChartPage()
TemperatureChartPage2() – 不太完善
方法 ChartDrawPage
import 'package:doluyo/dly_package/widget/dly_widget.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
显示图表使用 syncfusion_flutter_charts 插件实现
class TemperatureData {
TemperatureData(this.time, this.temperature);
final String time;
final double temperature;
}
class TempChart extends StatelessWidget {
const TempChart({super.key});
Widget build(BuildContext context) {
return MaterialApp(
home: ChartDrawPage('℃', '温度'),
);
}
}
//其他方案
class TemperatureChartPage extends StatefulWidget {
_TemperatureChartPageState createState() => _TemperatureChartPageState();
}
class _TemperatureChartPageState extends State<TemperatureChartPage> {
final List<TemperatureData> _temperatureData = [
TemperatureData('00:00', -20.5),
TemperatureData('01:00', -21.0),
TemperatureData('02:00', -22.5),
TemperatureData('03:00', -23.0),
TemperatureData('04:00', -24.5),
TemperatureData('05:00', -25.0),
TemperatureData('06:00', -26.5),
TemperatureData('07:00', -27.0),
TemperatureData('08:00', -28.5),
TemperatureData('09:00', -29.0),
TemperatureData('10:00', -30.5),
TemperatureData('11:00', -31.0),
TemperatureData('12:00', -32.5),
TemperatureData('13:00', -33.0),
TemperatureData('14:00', -34.5),
TemperatureData('15:00', -35.0),
TemperatureData('16:00', -34.5),
TemperatureData('17:00', -33.0),
TemperatureData('18:00', -32.5),
TemperatureData('19:00', -31.0),
TemperatureData('20:00', -30.5),
TemperatureData('21:00', -29.0),
TemperatureData('22:00', -28.5),
TemperatureData('23:00', -27.0),
];
double _visibleMinimum = 0;
double _visibleMaximum = 10;
void initState() {
super.initState();
// 设置初始可见范围为最后10个数据点
_visibleMinimum = _temperatureData.length - 10;
_visibleMaximum = _temperatureData.length.toDouble() - 1;
}
//实现左右滑动显示数据
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('温度曲线'),
),
body: Center(
child: GestureDetector(
onHorizontalDragUpdate: (details) {
double delta = details.delta.dx;
setState(() {
_visibleMinimum = (_visibleMinimum - delta / 10)
.clamp(0, _temperatureData.length - 10);
_visibleMaximum = _visibleMinimum + 9;
});
},
child: Container(
height: 200,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
child: SfCartesianChart(
//backgroundColor: Colors.white,
// zoomPanBehavior: ZoomPanBehavior(///图表不能滑动
// enablePanning: true,
// ),
primaryXAxis: CategoryAxis(
labelRotation: -45,
minimum: _visibleMinimum,
maximum: _visibleMaximum,
labelStyle: TextStyle(
color: Colors.black,
fontSize: 12,
),
axisLine: AxisLine(
color: Colors.grey,
width: 1,
),
majorGridLines: MajorGridLines(
// color: Colors.grey.withOpacity(0.3),
width: 0,
//dashArray: [5, 5], // 设置虚线
),
),
primaryYAxis: NumericAxis(
title: AxisTitle(
text: '温度 (°C)',
alignment: ChartAlignment.center,
),
labelStyle: TextStyle(
color: Colors.black,
fontSize: 12,
),
axisLine: AxisLine(
color: Colors.grey,
width: 1,
),
majorGridLines: MajorGridLines(
color: Colors.grey.withOpacity(0.3),
width: 1,
dashArray: [5, 5], // 设置虚线
),
),
tooltipBehavior: TooltipBehavior(enable: true),
series: <CartesianSeries>[
LineSeries<TemperatureData, String>(
dataSource: _temperatureData,
xValueMapper: (TemperatureData data, _) => data.time,
yValueMapper: (TemperatureData data, _) => data.temperature,
dataLabelSettings: DataLabelSettings(isVisible: true),
name: '温度',
markerSettings: MarkerSettings(isVisible: true),
color: Colors.blue,
width: 3,
//dashArray: [5, 5], // 设置虚线
),
],
),
).withPadding(EdgeInsets.all(20)),
),
),
);
}
///不能滑动查看数据
// @override
// Widget build(BuildContext context) {
// return Scaffold(
// appBar: AppBar(
// title: const Text('温度曲线'),
// ),
// body: Center(
// child: Container(
// height: 400,
// child: SfCartesianChart(
// zoomPanBehavior: ZoomPanBehavior(
// enablePanning: true,
// ),
// primaryXAxis: CategoryAxis(
// labelRotation: -45,
// ),
// primaryYAxis: NumericAxis(
// title: AxisTitle(text: '温度 (°C)'),
// ),
// tooltipBehavior: TooltipBehavior(enable: true),//点击查看详情
// series: <CartesianSeries>[
// LineSeries<TemperatureData, String>(
// dataSource: _temperatureData,
// xValueMapper: (TemperatureData data, _) => data.time,
// yValueMapper: (TemperatureData data, _) => data.temperature,
// dataLabelSettings: DataLabelSettings(isVisible: true),//显示点参数
// name: '温度',
// markerSettings: MarkerSettings(isVisible: true),//显示圆点
// ),
// ],
// ),
// ),
// ),
// );
// }
}
///不理想
class TemperatureChartPage2 extends StatefulWidget {
_TemperatureChartPageState2 createState() => _TemperatureChartPageState2();
}
class _TemperatureChartPageState2 extends State<TemperatureChartPage2> {
final List<TemperatureData> _temperatureData = [
TemperatureData('00:00', -20.5),
TemperatureData('01:00', -21.0),
TemperatureData('02:00', -22.5),
TemperatureData('03:00', -23.0),
TemperatureData('04:00', -24.5),
TemperatureData('05:00', -25.0),
TemperatureData('06:00', -26.5),
TemperatureData('07:00', -27.0),
TemperatureData('08:00', -28.5),
TemperatureData('09:00', -29.0),
TemperatureData('10:00', -30.5),
TemperatureData('11:00', -31.0),
TemperatureData('12:00', -32.5),
TemperatureData('13:00', -33.0),
TemperatureData('14:00', -34.5),
TemperatureData('15:00', -35.0),
TemperatureData('16:00', -34.5),
TemperatureData('17:00', -33.0),
TemperatureData('18:00', -32.5),
TemperatureData('19:00', -31.0),
TemperatureData('20:00', -30.5),
TemperatureData('21:00', -29.0),
TemperatureData('22:00', -28.5),
TemperatureData('23:00', -27.0),
];
double _visibleMinimum = 0;
double _visibleMaximum = 10;
void initState() {
super.initState();
_visibleMinimum = _temperatureData.length - 10;
_visibleMaximum = _temperatureData.length.toDouble() - 1;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('温度曲线'),
),
body: Center(
child: GestureDetector(
onHorizontalDragUpdate: (details) {
double delta = details.delta.dx;
setState(() {
_visibleMinimum = (_visibleMinimum - delta / 10)
.clamp(0, _temperatureData.length - 10);
_visibleMaximum = _visibleMinimum + 9;
});
},
child: Container(
height: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
child: LineChart(
LineChartData(
minX: _visibleMinimum,
maxX: _visibleMaximum,
minY: -40,
maxY: 0,
lineTouchData: LineTouchData(enabled: true),
gridData: FlGridData(
show: true,
drawHorizontalLine: true,
drawVerticalLine: true,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.3),
strokeWidth: 1,
dashArray: [5, 5],
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.3),
strokeWidth: 1,
dashArray: [5, 5],
);
},
),
borderData: FlBorderData(
show: true,
border: Border.all(
color: Colors.grey,
width: 1,
),
),
titlesData: FlTitlesData(
show: true,
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
// bottomTitles: SideTitles(
// showTitles: true,
// getTitles: (value) {
// int index = value.toInt();
// if (index >= 0 && index < _temperatureData.length) {
// return _temperatureData[index].time;
// }
// return '';
// },
// getTextStyles: (value) => TextStyle(
// color: Colors.black,
// fontSize: 12,
// ),
// margin: 10,
// ),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) =>
bottomTitleWidgets(value, meta),
reservedSize: 36,
interval: 1,
),
drawBelowEverything: true,
),
),
lineBarsData: [
LineChartBarData(
spots: _temperatureData
.asMap()
.entries
.map((entry) => FlSpot(
entry.key.toDouble(),
entry.value.temperature,
))
.toList(),
isCurved: true,
barWidth: 3,
color: Colors.blue,
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 4,
color: Colors.blue,
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
belowBarData: BarAreaData(
show: true,
color: Colors.blue.withOpacity(0.3),
),
),
],
),
),
).withPadding(EdgeInsets.all(10)),
),
),
);
}
Widget bottomTitleWidgets(double value, TitleMeta meta) {
if (value % 1 != 0) {
return Container();
}
final style = TextStyle(
//color: AppColors.contentColorBlack,
fontWeight: FontWeight.bold,
//fontSize: min(18, 18 * chartWidth / 300),
);
return SideTitleWidget(
axisSide: meta.axisSide,
space: 16,
child: Text(meta.formattedValue, style: style),
);
}
}
///没效果
class TemperatureChartPage3 extends StatefulWidget {
_TemperatureChartPageState3 createState() => _TemperatureChartPageState3();
}
class _TemperatureChartPageState3 extends State<TemperatureChartPage3> {
final List<TemperatureData> _temperatureData = [
TemperatureData('00:00', -20.5),
TemperatureData('01:00', -21.0),
TemperatureData('02:00', -22.5),
TemperatureData('03:00', -23.0),
TemperatureData('04:00', -24.5),
TemperatureData('05:00', -25.0),
TemperatureData('06:00', -26.5),
TemperatureData('07:00', -27.0),
TemperatureData('08:00', -28.5),
TemperatureData('09:00', -29.0),
TemperatureData('10:00', -30.5),
TemperatureData('11:00', -31.0),
TemperatureData('12:00', -32.5),
TemperatureData('13:00', -33.0),
TemperatureData('14:00', -34.5),
TemperatureData('15:00', -35.0),
TemperatureData('16:00', -34.5),
TemperatureData('17:00', -33.0),
TemperatureData('18:00', -32.5),
TemperatureData('19:00', -31.0),
TemperatureData('20:00', -30.5),
TemperatureData('21:00', -29.0),
TemperatureData('22:00', -28.5),
TemperatureData('23:00', -27.0),
];
double _visibleMinimum = 0;
double _visibleMaximum = 10;
void initState() {
super.initState();
_visibleMinimum = _temperatureData.length - 10;
_visibleMaximum = _temperatureData.length.toDouble() - 1;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('温度曲线'),
),
body: Center(
child: GestureDetector(
onHorizontalDragUpdate: (details) {
double delta = details.delta.dx;
setState(() {
_visibleMinimum = (_visibleMinimum - delta / 10)
.clamp(0, _temperatureData.length - 10);
_visibleMaximum = _visibleMinimum + 9;
});
},
child: Container(
height: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
child: LineChart(
LineChartData(
minX: _visibleMinimum,
maxX: _visibleMaximum,
minY: -40,
maxY: 0,
lineTouchData: LineTouchData(enabled: true),
gridData: FlGridData(
show: true,
drawHorizontalLine: true,
drawVerticalLine: true,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.3),
strokeWidth: 1,
dashArray: [5, 5],
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.3),
strokeWidth: 1,
dashArray: [5, 5],
);
},
),
borderData: FlBorderData(
show: true,
border: Border.all(
color: Colors.grey,
width: 1,
),
),
titlesData: FlTitlesData(
show: true,
),
lineBarsData: [
LineChartBarData(
spots: _temperatureData
.asMap()
.entries
.map((entry) => FlSpot(
entry.key.toDouble(),
entry.value.temperature,
))
.toList(),
isCurved: true,
barWidth: 3,
color: Colors.blue,
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 4,
color: Colors.blue,
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
belowBarData: BarAreaData(
show: true,
color: Colors.blue.withOpacity(0.3),
),
),
],
),
),
),
),
),
);
}
}
///没效果
class TemperatureChartPage4 extends StatefulWidget {
_TemperatureChartPageState4 createState() => _TemperatureChartPageState4();
}
class _TemperatureChartPageState4 extends State<TemperatureChartPage4> {
final List<TemperatureData> _temperatureData = [
TemperatureData('00:00', -20.5),
TemperatureData('01:00', -21.0),
TemperatureData('02:00', -22.5),
TemperatureData('03:00', -23.0),
TemperatureData('04:00', -24.5),
TemperatureData('05:00', -25.0),
TemperatureData('06:00', -26.5),
TemperatureData('07:00', -27.0),
TemperatureData('08:00', -28.5),
TemperatureData('09:00', -29.0),
TemperatureData('10:00', -30.5),
TemperatureData('11:00', -31.0),
TemperatureData('12:00', -32.5),
TemperatureData('13:00', -33.0),
TemperatureData('14:00', -34.5),
TemperatureData('15:00', -35.0),
TemperatureData('16:00', -34.5),
TemperatureData('17:00', -33.0),
TemperatureData('18:00', -32.5),
TemperatureData('19:00', -31.0),
TemperatureData('20:00', -30.5),
TemperatureData('21:00', -29.0),
TemperatureData('22:00', -28.5),
TemperatureData('23:00', -27.0),
];
double _visibleMinimum = 0;
double _visibleMaximum = 10;
void initState() {
super.initState();
_visibleMinimum = _temperatureData.length - 10;
_visibleMaximum = _temperatureData.length.toDouble() - 1;
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('温度曲线'),
),
body: Center(
child: GestureDetector(
onHorizontalDragUpdate: (details) {
// 调整滑动的灵敏度,避免遮挡Y轴
double delta = details.delta.dx;
setState(() {
_visibleMinimum = (_visibleMinimum - delta / 20)
.clamp(0, _temperatureData.length - 10);
_visibleMaximum = _visibleMinimum + 9;
});
},
child: Container(
height: 400,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.5),
spreadRadius: 5,
blurRadius: 7,
offset: const Offset(0, 3),
),
],
),
child: LineChart(
LineChartData(
minX: _visibleMinimum,
maxX: _visibleMaximum,
minY: -40,
maxY: 0,
lineTouchData: LineTouchData(enabled: false),
gridData: FlGridData(
show: true,
drawHorizontalLine: true,
drawVerticalLine: true,
getDrawingHorizontalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.3),
strokeWidth: 1,
dashArray: [5, 5],
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: Colors.grey.withOpacity(0.3),
strokeWidth: 1,
dashArray: [5, 5],
);
},
),
borderData: FlBorderData(
show: true,
border: Border.all(
color: Colors.grey,
width: 1,
),
),
titlesData: FlTitlesData(
show: true,
topTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
rightTitles: const AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 50, // 为Y轴标题预留空间
getTitlesWidget: (value, meta) {
return SideTitleWidget(
axisSide: meta.axisSide,
child: Text('${value.toInt()}°C'),
);
},
),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
reservedSize: 30,
interval: 1,
getTitlesWidget: bottomTitleWidgets,
),
),
),
lineBarsData: [
LineChartBarData(
spots: _temperatureData
.asMap()
.entries
.map((entry) => FlSpot(
entry.key.toDouble(),
entry.value.temperature,
))
.toList(),
isCurved: true,
//是否曲线
barWidth: 3,
color: Colors.blue,
dotData: FlDotData(
show: true, //显示点
getDotPainter: (spot, percent, barData, index) {
//圆点装饰
return FlDotCirclePainter(
radius: 4,
color: Colors.blue,
strokeWidth: 2,
strokeColor: Colors.white,
);
},
),
belowBarData: BarAreaData(
//阴影
show: false,
//color: Colors.blue.withOpacity(0.3),
),
),
],
),
).withPadding(
EdgeInsets.only(left: 10, top: 10, right: 10),
),
).withPadding(
// 添加padding,确保Y轴不被遮挡
EdgeInsets.all(20),
),
),
),
);
}
Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
fontWeight: FontWeight.bold,
fontSize: 13,
//color: AppColors.contentColorBlack,
);
Widget text;
text = Text('${_temperatureData[value.toInt()].time}', style: style);
return SideTitleWidget(
axisSide: meta.axisSide,
child: text,
);
}
}
//其他方案
class ChartDrawPage extends StatefulWidget {
final String unit;
final String sTitle;
final List<TemperatureData>? list;
const ChartDrawPage(
this.unit,
this.sTitle, {
this.list = null,
Key? key,
}) : super(key: key);
_ChartDrawPageState createState() => _ChartDrawPageState();
}
class _ChartDrawPageState extends State<ChartDrawPage> {
late List<TemperatureData> _temperatureData = [
TemperatureData('00:00', -20.5),
TemperatureData('01:00', -21.0),
TemperatureData('02:00', -22.5),
TemperatureData('03:00', -23.0),
TemperatureData('04:00', -24.5),
TemperatureData('05:00', -25.0),
TemperatureData('06:00', -26.5),
TemperatureData('07:00', -27.0),
TemperatureData('08:00', -28.5),
TemperatureData('09:00', -29.0),
TemperatureData('10:00', -30.5),
TemperatureData('11:00', -31.0),
TemperatureData('12:00', -32.5),
TemperatureData('13:00', -33.0),
TemperatureData('14:00', -34.5),
TemperatureData('15:00', -35.0),
TemperatureData('16:00', -34.5),
TemperatureData('17:00', -33.0),
TemperatureData('18:00', -32.5),
TemperatureData('19:00', -31.0),
TemperatureData('20:00', -30.5),
TemperatureData('21:00', -29.0),
TemperatureData('22:00', -28.5),
TemperatureData('23:00', -27.0),
];
double _visibleMinimum = 0;
double _visibleMaximum = 10;
void initState() {
super.initState();
if (widget.list != null && widget.list!.isNotEmpty) {
_temperatureData = widget.list!;
}
// 设置初始可见范围为最后10个数据点
_visibleMinimum = _temperatureData.length - 10;
_visibleMaximum = _temperatureData.length.toDouble() - 1;
}
//实现左右滑动显示数据
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('温度曲线'),
),
body: Center(
child: (_temperatureData.length > 10)
? _build_chart2(context)
: _build_chart1(context),
),
);
}
///滑动查看数据
Widget _build_chart2(BuildContext context) {
return GestureDetector(
onHorizontalDragUpdate: (details) {
double delta = details.delta.dx;
setState(() {
_visibleMinimum = (_visibleMinimum - delta / 10)
.clamp(0, _temperatureData.length - 10);
_visibleMaximum = _visibleMinimum + 9;
});
},
child: SfCartesianChart(
//backgroundColor: Colors.white,
// zoomPanBehavior: ZoomPanBehavior(///图表不能滑动
// enablePanning: true,
// ),
primaryXAxis: CategoryAxis(
labelRotation: -45,
minimum: _visibleMinimum,
maximum: _visibleMaximum,
labelStyle: TextStyle(
color: Colors.black,
fontSize: 12,
),
axisLine: AxisLine(
color: Colors.grey,
width: 1,
),
majorGridLines: MajorGridLines(
// color: Colors.grey.withOpacity(0.3),
width: 0,
//dashArray: [5, 5], // 设置虚线
),
),
primaryYAxis: NumericAxis(
// title: AxisTitle(//标题说明
// text: '${widget.sTitle}(${widget.unit})',
// alignment: ChartAlignment.center,
// ),
labelStyle: TextStyle(
color: Colors.black,
fontSize: 12,
),
axisLine: AxisLine(
color: Colors.grey,
width: 1,
),
majorGridLines: MajorGridLines(
color: Colors.grey.withOpacity(0.3),
width: 1,
dashArray: [5, 5], // 设置虚线
),
),
tooltipBehavior: TooltipBehavior(enable: true),
series: <CartesianSeries>[
LineSeries<TemperatureData, String>(
dataSource: _temperatureData,
xValueMapper: (TemperatureData data, _) => data.time,
yValueMapper: (TemperatureData data, _) => data.temperature,
dataLabelSettings: DataLabelSettings(isVisible: true),
name: '${widget.sTitle}',
markerSettings: MarkerSettings(isVisible: true),
color: Colors.blue,
width: 3,
//dashArray: [5, 5], // 设置虚线
),
],
),
);
}
///不能滑动查看数据
Widget _build_chart1(BuildContext context) {
return SfCartesianChart(
zoomPanBehavior: ZoomPanBehavior(
enablePanning: true,
),
primaryXAxis: CategoryAxis(
labelRotation: -45,
),
// primaryYAxis: NumericAxis(
// title: AxisTitle(text: '温度 (°C)'),
// ),
tooltipBehavior: TooltipBehavior(enable: true),
//点击查看详情
series: <CartesianSeries>[
LineSeries<TemperatureData, String>(
dataSource: _temperatureData,
xValueMapper: (TemperatureData data, _) => data.time,
yValueMapper: (TemperatureData data, _) => data.temperature,
dataLabelSettings: DataLabelSettings(isVisible: true),
//显示点参数
name: '${widget.sTitle}',
markerSettings: MarkerSettings(isVisible: true),
color: Colors.blue,
width: 3,
),
],
);
}
}
插件
#图表使用--曲线 https://pub.dev/packages/fl_chart/versions
fl_chart: ^0.69.0
#图表--曲线 https://pub.dev/packages/syncfusion_flutter_charts
syncfusion_flutter_charts: ^27.2.4