加速 "charts_flutter" 折线图渲染

Speed up "charts_flutter" LineChart Rendering

我目前正在开发一个 flutter 应用程序,该应用程序将在折线图上实时绘制来自 BLE 设备的数据(其中数据将以示波器样式格式显示)。作为实时 BLE 数据的占位符,我使用定时器回调生成并绘制带有“charts_flutter”包的正弦波。到目前为止,一切似乎都在工作。然而,当更新周期小于 500 毫秒时,图表渲染似乎难以跟上(例如,将其设置为 200 毫秒会导致一切冻结)。在我开始尝试不同的包之前,我想看看是否有人有提示或建议来尝试加快速度。有可能我只是在状态管理等方面做错了,因为我对 flutter 没有那么多经验。代码很短,所以我在下面包含了两个项目文件。谢谢

main.dart

import 'package:flutter/material.dart';
import 'package:charts_flutter/flutter.dart';
import 'package:provider/provider.dart';

import 'package:tuple/tuple.dart';
import 'models/graph_data_model.dart';

void main() => runApp(new GraphDataApp());

class GraphDataApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<GraphDataModel>(
            create: (context) => GraphDataModel()),
      ],
      child: MaterialApp(
        title: 'Data Plotter',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        initialRoute: '/',
        routes: {
          '/': (context) => MyApp(),
        },
      ),
    );
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Data Plotter")),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GraphDataWidget(),
          ],
        ),
      ),
    );
  }
}

class GraphDataWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.all(5.0),
      child: SizedBox(
        height: 400.0,
        child: Selector<GraphDataModel, Tuple2<GraphDataModel, double>>(
          selector: (_, graphDataModel) {
            return Tuple2(graphDataModel, graphDataModel.elapsedTime);
          },
          builder: (context, selectorTuple, child) {
            GraphDataModel graphDataModel = selectorTuple.item1;
            return LineChart(
                graphDataModel.series.toList(),
                animate: false,
                domainAxis: NumericAxisSpec(
                    viewport: NumericExtents(graphDataModel.domainStart, graphDataModel.domainEnd))
            );
          },
        ),
      ),
    );
  }
}

graph_data_model.dart

import 'dart:async';
import 'dart:math';
import 'package:charts_flutter/flutter.dart';
import 'package:flutter/material.dart';

const UPDATE_INTERVAL = 500;
const MAX_TIME_INTERVAL = 10.0;
const MAX_DATA_POINTS = ((MAX_TIME_INTERVAL * 1000) / UPDATE_INTERVAL);

class GraphDataModel extends ChangeNotifier {
  bool _running = false;
  int _startTime;
  double elapsedTime;
  List<Series<PlotPoint, double>> series;

  GraphDataModel() {
    // initialize the data series
    series = [
      Series<PlotPoint, double>(
        id: 'Sine',
        colorFn: (_, __) => MaterialPalette.blue.shadeDefault,
        domainFn: (PlotPoint point, _) => point.time,
        measureFn: (PlotPoint point, _) => point.value,
        data: <PlotPoint>[PlotPoint(0, 0)],
      ),
    ];

    // initialize the start time and start the timer
    _startTime = DateTime.now().millisecondsSinceEpoch;
    Timer.periodic(Duration(milliseconds: UPDATE_INTERVAL), _timerCallback);
  }

  // getters
  num get domainStart => _running ? (series.first.data.first.time) : 0;
  num get domainEnd => _running ? (series.first.data.last.time) : 1;

  void _timerCallback(Timer t) {
    final currentTime = DateTime.now().millisecondsSinceEpoch;
    // find the elapsed time in seconds
    elapsedTime = (currentTime - _startTime).toDouble() / 1000.0;
    // find the sine value based on the elapsed time
    final yVal = sin((2.0 * pi * elapsedTime) / 10.0);

    // add the latest data point to the series
    series.first.data.add(PlotPoint(elapsedTime, yVal));
    // remove the first element in the list to create oscilloscope scrolling effect
    if(series.first.data.length > MAX_DATA_POINTS.toInt() || !_running) {
      series.first.data.removeAt(0);
    }
    _running = true;
    notifyListeners();
  }
}

class PlotPoint {
  final double time;
  final double value;

  PlotPoint(this.time, this.value);
}

我得出的结论是“charts_flutter”根本不适合实时数据。我最终通过扩展自定义画家编写了自己的示波器小部件,并且没有明显的性能问题。