DateTimeMultiLineChart.dart 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import 'package:fl_chart/fl_chart.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_habit/common/I18N.dart';
  4. import 'package:flutter_habit/provider/ThemeProvider.dart';
  5. import 'package:flutter_habit/common/utils/ConvertUtils.dart';
  6. import 'package:provider/provider.dart';
  7. class DateTimeMultiLineChart extends StatelessWidget {
  8. final Map<String, List<FlSpot>> lineNameSportsMap;
  9. final int? size;
  10. DateTimeMultiLineChart({
  11. required this.lineNameSportsMap,
  12. this.size,
  13. });
  14. List<LineChartBarData> getLineChartBarData(BuildContext context) {
  15. int i = 0;
  16. List<LineChartBarData> out = lineNameSportsMap.values.map((sports) {
  17. LineChartBarData lineChartBarData = LineChartBarData(
  18. spots: sports,
  19. // 圆滑
  20. isCurved: size! > 7,
  21. // 线条颜色
  22. color:
  23. Provider.of<ThemeProvider>(context, listen: false).otherColors[i]
  24. ,
  25. // 线条宽度
  26. barWidth: 3,
  27. // 起点和终点是否圆滑
  28. isStrokeCapRound: true,
  29. // 点配置
  30. dotData: FlDotData(
  31. show: size! < 30,
  32. // getDotColor: (spot, percent, barData) =>
  33. // Provider.of<ThemeProvider>(context, listen: false).otherColors[i],
  34. // dotSize: 3,
  35. ),
  36. belowBarData: BarAreaData(
  37. show: false,
  38. color:
  39. Theme.of(context).colorScheme.secondary.withOpacity(0.1 / (i + 1))
  40. ,
  41. ),
  42. );
  43. i = (i + 1) % (themeColors.length - 1);
  44. return lineChartBarData;
  45. }).toList();
  46. return out;
  47. }
  48. @override
  49. Widget build(BuildContext context) {
  50. double? maxY, minY, maxX, minX, xInterval, yInterval;
  51. bool isEmpty = false;
  52. lineNameSportsMap.values.forEach((i) {
  53. if (i.isEmpty) {
  54. isEmpty = true;
  55. }
  56. });
  57. if (!isEmpty) {
  58. double now =
  59. ConvertUtils.localDaysSinceEpoch(DateTime.now()).floorToDouble();
  60. maxX = now;
  61. minX = now - size!;
  62. maxY = 24;
  63. minY = 0;
  64. switch (size) {
  65. case 7:
  66. xInterval = 1;
  67. break;
  68. case 30:
  69. xInterval = 5;
  70. break;
  71. case 90:
  72. xInterval = 18;
  73. break;
  74. default:
  75. xInterval = 1;
  76. break;
  77. }
  78. yInterval = 1;
  79. }
  80. int i = 0;
  81. return isEmpty
  82. ? Container(
  83. child: Column(
  84. children: <Widget>[
  85. Text(
  86. I18N.of("无数据"),
  87. style: Theme.of(context)
  88. .textTheme
  89. .displaySmall!
  90. .copyWith(color: Theme.of(context).colorScheme.secondary),
  91. ),
  92. Divider(),
  93. Row(
  94. mainAxisAlignment: MainAxisAlignment.end,
  95. children: <Widget>[
  96. Text(
  97. I18N.of("添加数据后会展示对应图表"),
  98. style: Theme.of(context).textTheme.bodySmall,
  99. ),
  100. ],
  101. ),
  102. ],
  103. ))
  104. : Column(
  105. crossAxisAlignment: CrossAxisAlignment.stretch,
  106. children: <Widget>[
  107. Row(
  108. mainAxisAlignment: MainAxisAlignment.end,
  109. children: lineNameSportsMap.keys.map((s) {
  110. // 右显数据
  111. Widget w = Text(
  112. "$s — ",
  113. style: TextStyle(
  114. color:
  115. Provider.of<ThemeProvider>(context, listen: false)
  116. .otherColors[i]),
  117. );
  118. i = (i + 1) % (themeColors.length - 1);
  119. return w;
  120. }).toList(),
  121. ),
  122. Container(
  123. height: 450,
  124. padding: EdgeInsets.only(top: 20, left: 10, right: 20),
  125. child: LineChart(
  126. LineChartData(
  127. // clipToBorder: true,
  128. // 边框信息
  129. borderData: FlBorderData(
  130. show: true,
  131. border: Border.all(
  132. color: Theme.of(context).colorScheme.secondary,
  133. width: 1,
  134. ),
  135. ),
  136. backgroundColor: Theme.of(context)
  137. .colorScheme
  138. .secondary
  139. .withOpacity(0.03),
  140. gridData: FlGridData(
  141. show: true,
  142. drawVerticalLine: true,
  143. drawHorizontalLine: true,
  144. // 从左到右每隔几个整数数据画条竖线
  145. verticalInterval: xInterval,
  146. // 横向网格线
  147. getDrawingHorizontalLine: (value) {
  148. return FlLine(
  149. color: Theme.of(context).colorScheme.secondary,
  150. strokeWidth: 0.5,
  151. );
  152. },
  153. horizontalInterval: yInterval,
  154. // 纵向网格线
  155. getDrawingVerticalLine: (value) {
  156. return FlLine(
  157. color: Theme.of(context).colorScheme.secondary,
  158. strokeWidth: 1,
  159. );
  160. },
  161. ),
  162. // 点击响应信息
  163. lineTouchData: LineTouchData(
  164. enabled: false,
  165. touchTooltipData: LineTouchTooltipData(
  166. tooltipBgColor: Theme.of(context)
  167. .colorScheme
  168. .secondary
  169. .withOpacity(0.2)
  170. .withAlpha(100),
  171. fitInsideHorizontally: true,
  172. ),
  173. ),
  174. titlesData: FlTitlesData(
  175. show: true,
  176. // 下方文字
  177. bottomTitles: AxisTitles(
  178. // 每隔几个显示一个底部标签
  179. // inte、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、rval: xInterval,
  180. // showTitles: true,
  181. // 文字与图表上边界距离
  182. // margin: 8,
  183. // 文字预留空间
  184. // reservedSize: 22,
  185. // textStyle: Theme.of(context).textTheme.bodyMedium,
  186. // getTitles: (value) {
  187. // DateTime dateTime =
  188. // ConvertUtils.dateTimeOfLocalDaysSinceEpoch(value);
  189. // return "${dateTime.month}-${dateTime.day}";
  190. // },
  191. ),
  192. leftTitles: AxisTitles(
  193. // 每隔几个显示一个左侧标签
  194. // interval: yInterval,
  195. // showTitles: true,
  196. // 文字与图表左边界距离
  197. // margin: 8,
  198. // 文字预留空间
  199. // reservedSize: 30,
  200. // textStyle: Theme.of(context).textTheme.bodyMedium,
  201. // getTitles: (value) {
  202. // int hour = 24 - value.floor();
  203. // if (hour < 10) {
  204. // return "0$hour:00";
  205. // }
  206. // return "$hour:00";
  207. // },
  208. ),
  209. ),
  210. // 各轴显示的最值 (不设置则缩放)
  211. minX: minX,
  212. maxX: maxX,
  213. maxY: maxY,
  214. minY: minY,
  215. // 数据
  216. lineBarsData: getLineChartBarData(context),
  217. ),
  218. ),
  219. )
  220. ],
  221. );
  222. }
  223. }