0%

Flutter 图表查看国家统计局的房价指数

最近做可视化数据要用到折线图表来展示数据,Flutter 中的图表库有 charts_flutterfl_chart 等,从易用性上最终决定了使用 fl_chart ,本文以国家统计局房价指数为数据来源,来实现一个 fl_chart 图表的小项目。

添加依赖

1
fl_chart: ^0.62.0

fl_chart 目前最新版本是 0.68,这里没有使用最新的版本是因为本地电脑的 dart 版本是 2.19.1,而最新版本 0.68 要求 dart 的最低版本是 3.2,考虑到需要兼容之前的项目,暂未升到最新版本。

数据来源

本文用到的数据是来自国家统计局公布的房价指数,这些数据在国家统计局官网上也能查看到,但可视化查看起来不是很方便,本文使用到的数据也只是其中一部分(如下图中的数据),目的是用来学习和研究,并没有用于其它任何商业用途。

4731716878015_.pic.png

LineChart

创建类 LineChartWidget ,在这里添加 LineChart 类。LineChartWidget 有三个属性,listItems 是要展示的数据,selectedCity 表示当前选中的城市,dates 是需要展示数据的月份数组,selectIndexType 当前展示的数据的类型,数据的类型有:同比指数、环比指数和定基指数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@immutable
class LineChartWidget extends StatelessWidget {
final List<ListItem> listItems;
final CityModel? selectedCity;
final List<String> dates;
final IndexType selectIndexType;

const LineChartWidget(
{Key? key,
required this.listItems,
required this.selectedCity,
required this.dates,
required this.selectIndexType})
: super(key: key);

@override
Widget build(BuildContext context) {
return LineChart(
mainData(),
);
}
}

LineChart 需要一个 LineChartData 参数,重点也就是这个 LineChartData,大部分关于线形图的参数设置在这个类里,第一次使用 fl_chart 看到这些参数也有点看不懂,下面介绍一下常用的几个参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
LineChartData(
// X 轴最大值
maxX: dates.length - 1,
// X 轴最小值
minX: 0,
// Y 轴的基线
baselineY: 0.0,
// X 轴最小值
minY: numMin,
// Y 轴最大值
maxY: numMax.ceilToDouble(),
// 数据点的数值提示,showIndexes 是需要显示提示的索引数组
showingTooltipIndicators: showIndexes.map((index) {
// ShowingTooltipIndicators 配置每个索引的数据点。
return ShowingTooltipIndicators([
LineBarSpot(lineBarsData[0], lineBarsData.indexOf(lineBarsData[0]),
lineBarsData[0].spots[index]),
]);
}).toList(),
// 纵用于设置网格线的样式。gridData 包含网格线的配置,如颜色、宽度、是否显示等
gridData: gridData,
// 用于设置图表的标题和标签,包括 X 轴和 Y 轴的标签样式和格式。
titlesData: titlesData,
// 用于配置触摸行为,点击数据点时的显示效果。lineTouchData 包含点击时的交互配置
lineTouchData: lineTouchData,
// 用于设置图表的边框样式。show 表示是否显示边框,border 设置边框的颜色和宽度。
borderData: FlBorderData(
show: true,
border: const Border(
left: BorderSide(color: Color(0xff313131)),
bottom: BorderSide(color: Color(0xff313131))),
),
// 用于配置额外的辅助线。这里配置了一条水平辅助线,颜色、宽度和虚线样式都可以自定义。
extraLinesData: ExtraLinesData(horizontalLines: [
HorizontalLine(
y: 0.0,
color: const Color(0xff010101),
strokeWidth: 1.6,
dashArray: [10, 2],
),
]),
// 图表的主要内容,包括折线数据。lineBarsData 包含一组 LineChartBarData 对象,每个对象代表一条折线及其数据点。
lineBarsData: lineBarsData,
)

对于在图上的各个配置

4751716880535_.pic.png

其中 lineBarsData 就是曲线图的内容了,要求返回一个数组,意思是可以同时显示多条曲线,我们这里只需要一条,点击勾选☑️来切换显示同比指数、环比指数或者定基指数。下面生成一个LineChartBarData 类并添加到数组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
LineChartBarData generateLineChartBarData(
{List<double> yValues = const [],
Color color = const Color(0xFF7351F6),
Color dotColor = const Color(0xFF002AFF)}) =>
LineChartBarData(
// true 表示曲线,false 表示折线
isCurved: true,
// 显示提示指示器的索引列表。这里通过 yValues转成map的keys数组的索引创建一个列表
showingIndicators: yValues.asMap().keys.toList(),
// 折线图上的数据点,由 FlSpot 类表示。每个 FlSpot 对象包含一个 x 和 y 值
spots: yValues.asMap().entries.map((e) {
return FlSpot(e.key.toDouble(), e.value);
}).toList(),
// 是否将折线连接处设置为圆角
isStrokeJoinRound: true,
// 折线的颜色
color: color,
// 折线的宽度
barWidth: 1.4,
// 是否将折线的末端设置为圆角
isStrokeCapRound: false,
// 配置折线上的数据点
dotData: FlDotData(
show: true,
getDotPainter: (spot, percent, barData, index) {
return FlDotCirclePainter(
radius: 2,
color: dotColor,
strokeWidth: 1.2,
strokeColor: dotColor,
);
}),
// 配置曲线下方区域的显示。
belowBarData: BarAreaData(
show: true,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [color.withAlpha(160), color.withAlpha(20)],
),
),
);

配置 titlesData,只显示左边和底部的 title

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
FlTitlesData get titlesData => FlTitlesData(
show: true,
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
bottomTitles: AxisTitles(
sideTitles: SideTitles(
reservedSize: 50,
showTitles: true,
interval: 1,
getTitlesWidget: bottomTitleWidgets,
),
),
leftTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: leftTitleWidgets,
),
),
);

Widget bottomTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
fontWeight: FontWeight.w300, fontSize: 11, color: Color(0xFF666666));
String date = dates[value.toInt()];
return SideTitleWidget(
axisSide: meta.axisSide,
angle: 30, // 旋转角度
space: 14,
child: Text(date, style: style),
);
}

Widget leftTitleWidgets(double value, TitleMeta meta) {
const style = TextStyle(
fontWeight: FontWeight.w300, fontSize: 11, color: Color(0xFF666666));
String text = NumberUtil.getNumByValueDouble(value, 1).toString();
return Text(text, style: style, textAlign: TextAlign.left);
}

设置网格线样式 gridData

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FlGridData get gridData => FlGridData(
show: true,
drawVerticalLine: true,
drawHorizontalLine: true,
horizontalInterval: 1,
verticalInterval: 1,
getDrawingHorizontalLine: (value) {
return FlLine(
color: const Color(0xffaeaeae),
strokeWidth: 0.5,
);
},
getDrawingVerticalLine: (value) {
return FlLine(
color: const Color(0xffaeaeae),
strokeWidth: 0.5,
);
},
);

设置 lineTouchData 触摸行为,也就是点击数据点时的显示效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
LineTouchData get lineTouchData => LineTouchData(
// 参数用于启用或禁用触摸事件
enabled: false,
// 这是一个回调函数,用于定义当数据点被触摸时的指示器样式。它返回 TouchedSpotIndicatorData 的列表,包含每个触摸点的指示器样式。
getTouchedSpotIndicator:
(LineChartBarData barData, List<int> spotIndexes) {
return spotIndexes.map((index) {
// 每个触摸点的指示器样式
return TouchedSpotIndicatorData(
FlLine(
// 为透明色,因此不会显示指示线
color: Colors.transparent,
),
FlDotData(
// 为 false,表示不显示默认的点
show: false,
),
);
}).toList();
},
// 参数用于配置触摸工具提示的显示样式。
touchTooltipData: LineTouchTooltipData(
// 设置为透明色,使工具提示背景透明。
tooltipBgColor: Colors.transparent,
tooltipRoundedRadius: 9,
tooltipPadding: const EdgeInsets.only(bottom: -10),
// 回调函数返回一个 LineTooltipItem 的列表,用于自定义工具提示的内容和样式。此处显示了触摸点的 Y 轴值,并设置了文本样式。
getTooltipItems: (List<LineBarSpot> lineBarsSpot) {
return lineBarsSpot.map((lineBarSpot) {
return LineTooltipItem(
lineBarSpot.y.toString(),
const TextStyle(
color: Color(0xFF212121),
fontSize: 14,
fontWeight: FontWeight.normal),
);
}).toList();
},
),
);

实现效果

来看看最终的实现效果

4741716880071_.pic _1_.jpg

小结

上面 LineChart 为例介绍了它使用方法和参数,fl_chart 还有 BarChartPieChart 等其它可视化图表,使用方法和参数上大同小异吧,用的时候如果还有不知道的可以去看看官方文档。fl_chart 在众多 Flutter 图表框架中使用人数还是比较多的,功能丰富,也能满足不少自定义的需求,但相对于原生 iOSAndroid 平台的图表框架,fl_chart 还是有一些不足和局限,如它不支持 xy 轴的滚动、Tooltip 仅支持基本的文本样式定制,不支持更复杂的布局和内容、对于非常复杂的图表,如多个折线、多层次的图表组合,fl_chart 的性能可能不足以满足需求,渲染时间会明显增加等等,当然这些功能的完善都需要一个过程。

以上的开源代码在这里:源码 供大家参考,本文虽然没有特别复杂的原理和逻辑,但是从调试数据、翻看整理文档、代码实现和细节调整,这样的一个小项目也是花了不少时间和精力,希望能够帮到你,感谢阅读,记得加关注和点赞哦。