为您的应用程序引入反应性图表颤振小部件

介绍反应式Echarts Flutter Widget的开发工作:

随着其快速发展,Flutter已被应用于越来越多的大型项目,而复杂的数据可视化图表已成为重要的要求。 尽管Flutter具有强大的类(例如Painter或Canvas)来进行绘画工作,但不幸的是,在Flutter生态系统中没有任何杀手级数据可视化库。

今年年初,Flutter开发团队发布了一个官方的内联WebView小部件: webview_flutter 。 它基于新的“平台视图”,这使得可以像其他小部件一样无缝地将Web内容嵌入Flutter中。 因此,我们可以将那些成熟的Web数据可视化库导入到Flutter应用中。

当谈到成熟,强大且易于使用的数据可视化库时, Echarts无疑是一个不错的选择。 我在这里不再赘述。 如果我们可以将Echarts添加到Flutter应用程序中,我们不仅可以实现其支持的丰富图表类型,还可以重用Web上现成的图表代码以减少工作量。

因此,我们封装了Flutter小部件: flutter_echarts ,同时考虑了可扩展性和易用性,从而帮助Flutter开发人员充分发挥Echarts的功能。

特征

在此之前,我们将Echarts组件封装在React Native中,并获得了一些有关如何在反应式UI框架中使用数据可视化库的经验,因此,对于Flutter,我们为flutter_echarts设计了一些功能:

反应性更新

与所有其他反应式UI框架一样,Flutter的最重要功能之一是,它会根据数据的变化自动更新视图,这为开发带来了很多便利。 Echarts独立于任何UI框架,但其设计是由数据驱动的,并且数据的变化驱动图表中的变化。

因此,我们只需要将Echarts的数据驱动方法与Flutter的视图更新连接起来即可实现小部件的反应式更新。 在Echarts中设置动态数据更新非常简单。 所有数据更新都是通过setOption进行的。 您只需要获取所需的数据,将数据填充到setOption中,而无需考虑数据带来的更改,ECharts会发现两组数据之间的差异,并通过适当的动画呈现差异。

同时,在Flutter中,当容器窗口小部件更新并且传递给子窗口小部件的数据属性发生更改时,将触发此StatefulWidget的State.didUpdateWidget。 因此,在其中调用setOption将通知Echarts更新图表。 这使得flutter_echarts就像简单的StatelessWidget一样易于使用。

双向通讯

图表与外部程序之间的通信是非常必要的。 在flutter_echarts中,JavaScript和Dart之间的通信原理就像父子部件之间的通信原理:“道具关闭,事件增加”。

来自外部的所有设置和命令都通过option和extraScript以JavaScript代码字符串的形式传递到图表。 这些代码将由WebView执行; 另一方面,WebView内部的事件通过JavascriptChannel发送并由onMessage函数处理。 这是内部JavaScript和外部Dart之间的两种通信方式。

可配置的扩展

Echarts具有各种扩展 ,包括图表,地图和WebGL。 在网络中,我们可以将它们作为脚本文件导入以扩展Echarts的功能。 对于开箱即用的用法,flutter_echarts嵌入了最新版本的Echarts脚本,不需要额外的导入。 同时,我们向用户公开了extensions属性,以包括所需的任何脚本。 extensions是一个字符串列表,用户可以直接将脚本字符串复制到源代码中,从而避免了文件读取和复杂的资产目录。

小部件属性

在封装小部件时,易于使用通常比完整性更重要。 它应使所有级别的开发人员都可以立即使用。 自己绘制图表的目的是为了易于理解,并尝试将所有配置都置于选项中( 本文中的详细信息 )。 因此flutter_echarts还简化了小部件属性:

选项

图表的JavaScript Echarts选项作为字符串。 echarts主要由此属性配置。 您可以在dart:convert中使用jsonEncode()函数以Dart对象形式转换数据:

source: ${jsonEncode(_data1)},

因为JavaScript没有''',所以您可以使用此运算符减少配额的某些转义运算符:

Echarts(
  option: '' '
  
    // option string
    
  ' '' ,
),

extraScript

将在Echarts.init()之后和任何chart.setOption()之前执行的JavaScript。 该小部件已构建了一个名为Messager的javascriptChannel,因此您可以使用此标识符将消息从JavaScript发送到Flutter:

extraScript: '' '
  chart.on(' click ', (params) => {
  if(params.componentType === ' series ') {
  	Messager.postMessage(' anything ');
  }
  });
' '' ,

onMessage

无效函数(字符串)

处理extraScript中Messager.postMessage()发送的消息的函数。

扩展名

列表<字符串>

从Echarts扩展中提取的字符串列表,例如组件,WebGL,语言等。您可以在此处下载。 将它们作为原始字符串插入:

const liquidPlugin = r '' '

  // copy from liquid.min.js

' '' ;

captureAllGestures

布尔

(默认值:false)

图表是否捕获所有手势。 在处理3D旋转和数据缩放条时,进行设置很有用。 请注意,这将阻止ListViews之类的容器作用于图表上的手势。

这些是flutter_echarts的全部4个属性。 诸如何时更新图表之类的其他事情是由内部机制完成的。 这使得flutter_echarts看起来就像一个简单的演示性StatelessWidget。 用户只需要熟悉Echarts,无需额外学习。

完整的示例在这里: flutter_echarts_example

当然,如果您有任何建议或要求,请提出问题

源代码分析

加载HTML

对于跨平台开发,由于操作系统的文件系统不同,因此总是很难管理资产目录。 在React Native中,有时甚至需要手动将html文件复制到Android目录中。 Flutter具有完整的资产系统,但还需要额外的依赖关系和配置。 因此,在源代码中将本地html作为文本字符串加载是个好主意,并且webview_flutter团队也在其官方示例中推荐了这种方式。

因此,我们在小部件的初始化过程中将所有模板html,Echarts脚本,扩展脚本和初始代码放入字符串中,并将其作为uri源加载:

  @override
  void initState() {
    super .initState();
    _htmlBase64 = 'data:text/html;base64,' + base64Encode(
      const Utf8Encoder().convert(_getHtml(
        echartsScript,
        widget.extensions ?? [],
        widget.extraScript ?? '' ,
      ))
    );
    _currentOption = widget.option;
  }
  
  ...
  
  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: _htmlBase64,
      
      ...
    );
  }

请注意,与uri字符串中一样,有一些有限的字符,因此我们将字符串编码为Base64。

有个提示:JavaScript不能包含''',因此我们可以用它包装JavaScript字符串以减少一些转义工作。

更新图表

反应式更新的基本机制是在State.didUpdateWidget挂钩中调用setOption来通知图表更新:

  void update( String preOption) async {
    _currentOption = widget.option;
    if (_currentOption != preOption) {
      await _controller?.evaluateJavascript( '' '
        chart && chart.setOption($_currentOption, true);
      ' '' );
    }
  }

  @override
  void didUpdateWidget(Echarts oldWidget) {
    super .didUpdateWidget(oldWidget);
    update(oldWidget.option);
  }

最麻烦的部分是小部件的初始化。

我们知道WebView中html的加载和数据的获取都是异步的,并且我们不知道哪个会更早完成。 WebView初始化中的生命周期顺序为:

onWebViewCreated --> loading html --> onPageFinished

而且只能在onWebViewCreated中访问WebViewController。 换句话说,当窗口小部件获得WebViewController时,我们无法确定html是否已加载,因此在didUpdateWidget中,我们无法通过测试WebViewController来确定它是否已准备好进行更新。

我们的解决方案是将“数据道具更改触发图表更新”分离为两个步骤:“数据道具更改导致_currentOption更改”和“根据_currentOption更新图表”,这确保即使在加载html之前也记录了任何数据。 。

  String _currentOption;
  
  void init() async {
    await _controller?.evaluateJavascript( '' '
      chart.setOption($_currentOption, true);
    ' '' );
  }

  void update( String preOption) async {
    _currentOption = widget.option;
    ...
  }
  
  @override
  Widget build(BuildContext context) {
    return WebView(
      ...
      onPageFinished: ( String url) {
        init();
      },
      ...
    );
  }

讯息频道

webview_flutter提供了javascriptChannels属性来设置多个命名频道。 但是考虑到不了解webview_flutter的用户,flutter_echarts不会公开此属性。 相反,我们仅构建一个名为“ Messager”的通道:

  @override
  Widget build(BuildContext context) {
    return WebView(
      ...
      javascriptChannels: < JavascriptChannel > [
        JavascriptChannel(
          name: 'Messager',
          onMessageReceived: (JavascriptMessage javascriptMessage) {
            widget?.onMessage(javascriptMessage.message);
          }
        ),
      ].toSet(),
    );
  }

如果需要发送多种类型的事件,则用户可以像在redux中那样创建动作:

chart.on( 'click' , (params) => {
  if (params.componentType === 'series' ) {
    Messager.postMessage( JSON .stringify({
      type : 'select' ,
      payload : params.dataIndex,
    }));
  }
});

敬请关注!

先前发布在https://medium.com/analytics-vidhya/reactive-echarts-flutter-widget-fedab7f3c52f


From: https://hackernoon.com/introducing-reactive-echarts-flutter-widget-for-your-application-ikji36ln

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值