Flutter实时刷新折线图实现

背景:

最近有个项目需求,需要以图表的方式展示实时采集的脑神经数据。数据比较多,以1k的采样率来算,乘以采集通道数,大致需要每秒刷新万级以上的数据点。

正常这种图表一般都是在matlab上展示分析的。不过为了一些其它功能吧,需要在pad上也实现这个功能。之前在ipad上使用三方库有实现过差不多的需求功能,不过这次的项目要求是在android pad上使用。本人不喜欢android原生开发,但对flutter还是比较喜欢的,自然就采用了flutter开发。

工具调研

欲先成其事,必先利其器。磨刀不误砍柴功,遂先找了找flutter的图表库。寻了一遍,flutter图表库用的比较多的着实比较少,只有两个fl_chart和graphic,还有个syncfusion_officechart,不过这个是付费版的(也能免费用,但是条件限制很苛刻)。剩下还有一些用的人不太多的库,没有太多研究。

直接上手模拟生成了一堆数据,然后丢进了fl_chart。然后图是出来了,在50ms刷新频率,上万数据点的显示下,图表卡成了幻灯片。一看fps在10左右徘徊。换成graphic,效果稍微好了些,but依旧很卡,看了下cpu消耗。大多数cpu都消耗在了x轴取title的操作上,这里因为graphic数据格式定义的关系,当数据多了还有个性能bug。比如1w个点,就要做1w次循环从1w的数据里找title。直接炸裂,做个map,从map里取应该会提升很多吧。不过我的需求比较简单,不需要显示这么复杂的x轴title。于是直接改库源码,换成index作为x title。性能提升到了25左右的fps。看着满载的cpu和raster负荷。我陷入了沉思。

自己动手丰衣足食

我的需求只需要展示出数据变动的曲线,而交互什么的在这么高的频率刷新下是没有需求的。于是决定自己画个图表。

看了下flutter的canvas绘制,貌似还挺简单。甚至提供了直接通过points画线的功能。

arduino 复制代码
void drawPoints(PointMode pointMode, List<Offset> points, Paint paint);

enum PointMode {
  points,
  lines,
  polygon,
}

于是一切都简单了,改吧改吧,一个简单的手写版折线图就出来了。 大致贴个代码

scala 复制代码
class WSChartView extends StatelessWidget {

  const WSChartView(this.config, this.points, {super.key});

  final List<Offset> points;

  final ChartConfig config;

  @override

  Widget build(BuildContext context) {

    return Stack(

      children: [

        if (config.yTitles != null)

          SizedBox(

            width: 32,

            child: Column(

                mainAxisAlignment: MainAxisAlignment.spaceBetween,

                children: config.yTitles!.map((e) => Text(e)).toList()),

          ),

        if (config.yTitles != null)

          SizedBox.expand(

              child: RepaintBoundary(

                  child: CustomPaint(

            painter: ChartXYPainter(ChartXYConfig(yTitles: config.yTitles)),

          ))),

        SizedBox.expand(

            child: Container(

                padding: EdgeInsets.fromLTRB(

                    config.yTitles != null ? 32 : 8, 8, 8, 8),

                child: RepaintBoundary(

                  child: CustomPaint(

                    painter: _WSPainter(points, config),

                  ),

                )))

      ],

    );

  }

}

class _WSPainter extends CustomPainter {

  _WSPainter(this.points, this.config) {

    _paint = Paint();

    _paint.strokeWidth = 1;

    _paint.color = config.color;

  }

  

  final List<Offset> points;

  final ChartConfig config;

  late Paint _paint;

  

  @override

  void paint(Canvas canvas, Size size) {

    var length = points.length;

    var data = <Offset>[];

    final lastX = points.last.dx;

    var xPercent = size.width / lastX;

    var yPercent = size.height / 2 / config.yMax;

    for (var i = 0; i < length; i++) {

      var point = points[i];

      data.add(

          Offset(point.dx * xPercent, (config.yMax - point.dy) * yPercent));

    }

    canvas.drawPoints(PointMode.polygon, data, _paint);

  }

  

  @override

  bool shouldRepaint(covariant _WSPainter oldDelegate) {

    return true;

  }

}

class ChartXYPainter extends CustomPainter {

  ChartXYPainter(this.config) {

    _paint = Paint();

    _paint.strokeWidth = 2;

    _paint.color = Colors.black;

  }

  

  late Paint _paint;

  

  final ChartXYConfig config;

  

  @override

  void paint(Canvas canvas, Size size) {

    final edge = (

      8.0,

      8.0,

      config.xTitles != null ? 32.0 : 8.0,

      config.yTitles != null ? 32.0 : 8.0

    );

    canvas.drawLine(Offset(edge.$4, size.height - edge.$3),

        Offset(size.width - edge.$2, size.height - edge.$3), _paint);

    canvas.drawLine(Offset(edge.$4, size.height - edge.$3),

        Offset(edge.$4, edge.$1), _paint);

  }

  

  @override

  bool shouldRepaint(covariant ChartXYPainter oldDelegate) {

    return false;

  }

}

ChartXYPainter 对应的是画xy轴的功能。

代码简洁,功能也挺简单,后续有对应的刷新实时要求高的图表都可以用这种方式实现。性能会好很多。

相关推荐
火柴就是我1 天前
flutter 之真手势冲突处理
android·flutter
Speed1231 天前
`mockito` 的核心“打桩”规则
flutter·dart
法的空间1 天前
Flutter JsonToDart 支持 JsonSchema
android·flutter·ios
恋猫de小郭1 天前
Android 将强制应用使用主题图标,你怎么看?
android·前端·flutter
玲珑Felone1 天前
从flutter源码看其渲染机制
android·flutter
ALLIN2 天前
Flutter 三种方式实现页面切换后保持原页面状态
flutter
Dabei2 天前
Flutter 国际化
flutter
Dabei2 天前
Flutter MQTT 通信文档
flutter
Dabei2 天前
Flutter 中实现 TCP 通信
flutter
孤鸿玉2 天前
ios flutter_echarts 不在当前屏幕 白屏修复
flutter