【Flutter】graphic图表的快速上手

news2024/9/27 23:33:28

简介

graphic是一个数据可视化语法和Flutter图表库。

在这里插入图片描述
官方github示例

网上可用资源很少,只有作者的几篇文章,并且没有特别详细的文档,使用的话还是需要一定的时间去调研,在此简单记录。

示例

以折线图为例(因为我只用到了折线图,但其他的图大差不差)

创建一个两个文件:linePage.dart和数据文件data.dart
创建main.dart,引入linePage.dart

// main.dart
import 'package:flutter/material.dart';
import './linePage.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
   
  const MyApp({
   super.key});

  @override
  Widget build(BuildContext context) {
   
    return MaterialApp(
        title: 'Flutter Graphic Example',
        debugShowCheckedModeBanner: false,
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Flutter Graphic Example'),
          ),
          body: linePage(),
        ));
  }
}

// linePage.dart 
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:graphic/graphic.dart';
import 'package:intl/intl.dart';

import './data.dart';

class linePage extends StatelessWidget {
   
  linePage({
   super.key});

  final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();

  @override
  Widget build(BuildContext context) {
   
    return SingleChildScrollView(
      child: Center(
        child: Column(
          children: <Widget>[
            Container(
              padding: const EdgeInsets.fromLTRB(20, 40, 20, 5),
              child: const Text(
                'Smooth Line and Area chart', //单线
                style: TextStyle(fontSize: 20),
              ),
            ),
            Container(
              margin: const EdgeInsets.only(top: 10),
              width: 350,
              height: 300,
              child: Chart(
                // 数据源
                data: invalidData,
                // 变量配置
                variables: {
   
                  'date': Variable(
                    accessor: (Map map) => map['date'] as String,
                    scale: OrdinalScale(
                      tickCount: 5, // x轴刻度数量
                    ),
                  ),
                  'points': Variable(
                  // 数据值
                    accessor: (Map map) => (map['points'] ?? double.nan) as num,
                  ),
                },

                marks: [
                  // 线条与X轴之间区域填充
                  AreaMark(
                    shape: ShapeEncode(
                      value:
                          BasicAreaShape(smooth: true), // smooth: true 使线条变得平滑
                    ),
                    color: ColorEncode(
                      value: Colors.pink.withAlpha(80),
                    ),
                  ),
                  // 线条
                  LineMark(
                    shape: ShapeEncode(
                      value: BasicLineShape(smooth: true),
                    ),
                    // 粗细
                    size: SizeEncode(value: 1.5),
                  ),
                ],
                // 坐标轴配置
                axes: [
                  Defaults.horizontalAxis,
                  Defaults.verticalAxis,
                ],
                /** 选项配置
                * 可以写多个,提供给tooltip单独配置selections:{‘touchMove’}选择
                * 或者marks中LineMark的color属性的updaters
                */
                selections: {
   
                // 'touchMove'名称随意起,一般与是功能性描述词
                  'touchMove': PointSelection(
                    on: {
    //on里面配置操作选项
                      GestureType.scaleUpdate, // 可垂直或水平移动准线
                      GestureType.tapDown,     // 点击
                      GestureType.longPressMoveUpdate  //长按拖动
                    },
                    dim: Dim.x,
                  )
                },
                // 触摸弹框提示
                tooltip: TooltipGuide(
                  //未单独配置 selections,默认使用上面的selections中配置
                  // 跟随鼠标位置(感觉主要是看第二项是true,tooltip框才会跟随,false会随着鼠标移动乱动)
                  followPointer: [false, true],
                  align: Alignment.topLeft, // tooltip弹框对于点击位置的对齐方式
                  offset: const Offset(-20, -20), //  位置偏移量,结合align
                ),
                // 十字准线
                crosshair: CrosshairGuide(followPointer: [false, true]),
              ),
            ),
            Container(
              padding: const EdgeInsets.fromLTRB(20, 40, 20, 5),
              child: const Text(
                'Group interactions', //多线
                style: TextStyle(fontSize: 20),
              ),
            ),
            Container(
              margin: const EdgeInsets.only(top: 10),
              width: 350,
              height: 300,
              child: Chart(
                data: invalidData1,
                // 根据给定线条数据的name值相同的为同一条线
                variables: {
   
                  'date': Variable(
                    accessor: (Map map) => map['date'] as String,
                    scale: OrdinalScale(tickCount: 5, inflate: true),
                  ),
                  'points': Variable(
                    accessor: (Map map) => (map['points'] ?? double.nan) as num,
                  ),
                  'name': Variable(
                    accessor: (Map map) => map['name'] as String,
                  ),
                },
                coord: RectCoord(horizontalRange: [0.1, 0.99]),
                marks: [
                  LineMark(
                    position:
                        Varset('date') * Varset('points') / Varset('name'),
                    shape: ShapeEncode(value: BasicLineShape(smooth: true)),
                    size: SizeEncode(value: 1.5),
                    color: ColorEncode(
                      variable: 'name',
                      values: Defaults.colors10,
                      // 改变线条颜色
                      // updaters: {
   
                      // 'groupMouse'是在selections中配置的
                      //   'groupMouse': {false: (color) => color.withAlpha(100)},
                      //   // 'groupTouch': {false: (color) => color.withAlpha(100)},
                      // },
                    ),
                  ),
                  // 显示线条上的数据点
                  // PointMark(
                  //   color: ColorEncode(
                  //     variable: 'name',
                  //     values: Defaults.colors10,
                  //     updaters: {
   
                  //       'groupMouse': {false: (color) => color.withAlpha(100)},
                  //       'groupTouch': {false: (color) => color.withAlpha(100)},
                  //     },
                  //   ),
                  // ),
                ],
                axes: [
                  Defaults.horizontalAxis,
                  Defaults.verticalAxis,
                ],
                // // 提示框选项配置
                selections: {
   
                  '666': PointSelection(
                    on: {
   GestureType.hover, GestureType.tap},
                    // 设备[mouse(鼠标),stylus(手写笔),invertedStylus,trackpad(触控板),touch(触摸屏),unknown]
                    // 参考资料:https://api.flutter.dev/flutter/dart-ui/PointerDeviceKind.html
                    devices: {
   
                    	PointerDeviceKind.mouse // 鼠标 (该配置在鼠标设备时生效)
                    },
                    // 显示此处date相同的数据
                    variable: 'date',
                  ),
                  'groupMouse': PointSelection(
                  	// 触发的交互
                  	// 参考资料:https://pub.dev/documentation/keyboard_dismisser/latest/keyboard_dismisser/GestureType.html
                    on: {
   
                      GestureType.hover, // 覆盖
                    },
                    // variable: 'name',
                    devices: {
   PointerDeviceKind.mouse},
                  ),
                  'tooltipTouch': PointSelection(
                    on: {
   
                      GestureType.scaleUpdate, 
                      GestureType.tapDown, //点击
                      GestureType.longPressMoveUpdate 
                    },
                    // variable: 'name',
                    devices: {
   
                    	PointerDeviceKind.touch,  // 触摸屏(仅在触摸设备生效:神奇的是不包括iOS)
                    },
                  ),
                  'tooltipTouchIos': PointSelection(
                    on: {
   
                      GestureType.scaleUpdate,
                      GestureType.tapDown,
                      GestureType.longPressMoveUpdate
                    },
                    // variable: 'name',
                    devices: {
   
                    	// 未知设备(不明白为啥iOS被识别成了unknown,猜测可能与ios中的触摸事件有关)
                    	PointerDeviceKind.unknown, 
                    },
                  ),
                },
                tooltip: TooltipGuide(
                  // 选择触发配置
                  selections: {
   'tooltipTouch', '666'},
                  followPointer: [false, true],
                  align: Alignment.topLeft,
                  // tooltip中显示的内容(按顺序显示)
                  // 与上方selections中定义的variable相排斥
                  variables: [
                    // 'date',
                    'name',
                    'points',
                  ],
                ),
                // 十字准线配置
                crosshair: CrosshairGuide(
                  selections: {
   'tooltipTouch', '666'},
                  styles: [
                    PaintStyle(
                        strokeColor: const Color.fromARGB(255, 92, 68, 68)),
                    PaintStyle(
                        strokeColor: const Color.fromARGB(0, 158, 154, 154)),
                  ],
                  followPointer: [false, true],
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

数据文件

// data.dart
const invalidData1 = [
  {
   "date": "2016-01-04", "name": "线条1", "points": 126.12},
  {
   "date": "2016-01-05", "name": "线条1", "points": 125.688},
  {
   "date": "2016-01-06", "name": "线条1", "points": 119.704},
  {
   "date": "2016-01-07", "name": "线条1", "points": 120.19},
  {
   "date": "2016-01-08", "name": "线条1", "points": 121.157},
  {
   "date": "2016-01-11", "name": "线条1", "points": 117},
  {
   "date": "2016-01-12", "name": "线条1", "points": 120},
  {
   "date": "2016-01-13", "name": "线条1", "points": 122},
  {
   "date": "2016-01-14", "name": "线条1", "points": 117.76},
  {
   "date": "2016-01-15", "name": "线条1", "points": 114.397},
  {
   "date": "2016-01-18", "name": "线条1", "points": 116.373},
  {
   "date": "2016-01-19", "name": "线条1", "points": 120.547},
  {
   "date": "2016-01-20", "name": "线条1", "points": 113.733},
  {
   "date": "2016-01-21", "name": "线条1", "points": 114.098},
  {
   "date": "2016-01-22", "name": "线条1", "points": 123.833},
  {
   "date": "2016-01-25", "name": "线条1", "points": 125},
  {
   "date": "2016-01-26", "name": "线条1", "points": 124.866},
  {
   "date": "2016-01-27", "name": "线条1", "points": 120.264},
  {
   "date": "2016-01-28", "name": "线条1", "points": 122.296},
  {
   "date": "2016-01-29", "name": "线条1", "points": 124.502},
  {
   "date": "2016-02-01", "name": "线条1", "points": 127.936},
  {
   "date": "2016-02-02", "name": "线条1", "points": null},
  {
   "date": "2016-02-03", "name": "线条1", "points": 129.95},
  {
   "date": "2016-02-04", "name": "线条1", "points": 129.275},
  {
   "date": "2016-02-05"

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1262633.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

解锁 ElasticJob 云原生实践的难题

发生了什么 最近在逛 ElasticJob 官方社区时发现很多小伙伴都在头疼这个 ElasticJob 上云的问题&#xff0c;ElasticJob 本就号称分布式弹性任务调度框架&#xff0c;怎么在云原生环境就有了问题了呢&#xff0c;这就要从 Kubenertes 和 ElasticJob 的一些状态化说起。 有意思的…

Unity打出的安卓包切换后台再恢复前台,卡顿许久问题记录

连接AndroidStudio发现当切换后台时提示&#xff1a;D/Unity: Multi-casting "[IP] 192.168.31.231 [Port] 55000 [Flags] 19 [Guid] 1268732307 [EditorId] 264356214 [Version] 1048832 [Id] AndroidPlayer(11,Xiaomi_M2012K11AC192.168.31.231) [Debug] 0 [PackageName…

Python中如何用栈实现队列

目录 一、引言 二、使用两个栈实现队列 三、性能分析 四、应用场景 五、代码示例 六、优缺点总结 一、引言 队列&#xff08;Queue&#xff09;和栈&#xff08;Stack&#xff09;是计算机科学中常用的数据结构。队列是一种特殊的线性表&#xff0c;只允许在表的前端进行…

简易版扫雷+代码分析

前言&#xff1a; 实验一个简易版的扫雷&#xff0c;也要两百来行的代码&#xff0c;因此为了代码整洁&#xff0c;维护起来方便&#xff0c;这里我们和前期实现的三子棋一样&#xff0c;也弄一个游戏的头文件game.h用来装各种头文件以及函数的声明以及宏定义、预处理信息&…

虚幻学习笔记5—UI预设体制作

一、前言 本文使用的虚幻引擎5.3.2&#xff0c;在unity中有预设体的概念&#xff0c;可以将一个组合型的物体或UI制作成预设体&#xff0c;方便后续可以快速制作更多元的内容和复用。虚幻本身没有这个概念&#xff0c;但是要实现类似的效果其&#xff0c;故此我引用了这个概念。…

羊大师介绍,备孕阶段饮食规划及对羊奶的影响

备孕期是夫妻俩为了生育健康宝宝所准备的重要阶段&#xff0c;在这个阶段&#xff0c;营养的摄入对于双方的身体健康和胚胎的发育至关重要。而羊奶作为一种营养丰富的饮品&#xff0c;备孕期间是否能喝羊奶一直是备孕夫妇们关注的话题。本文小编羊大师将会详细解答这一问题&…

采集工具-免费采集器下载

在当今信息时代&#xff0c;互联网已成为人们获取信息的主要渠道之一。对于研究者和开发者来说&#xff0c;如何快速准确地采集整个网站数据是至关重要的一环。以下将从九个方面详细探讨这一问题。 确定采集目标 在着手采集之前&#xff0c;明确目标至关重要。这有助于确定采集…

算法—双指针

双指针算法可以帮忙把时间复杂度降低一个维度&#xff0c;即原本O&#xff08;n2&#xff09;降为O(n)&#xff1b;将O(n)降为O(1) 移动零 移动零 题目解析 将所有0移动到末尾保持非0元素相对顺序对数组进行原地操作&#xff08;不开辟额外空间&#xff09; 算法原理 数组…

什么游戏搬砖挣钱,还不费时间?

游戏搬砖的项目挺多的&#xff0c;但是不费时间&#xff1f;估计就Steam搬砖或叫CSGO搬砖。 正常的游戏搬砖的项目&#xff0c;想要挣钱&#xff0c;没有不费时间的。因为游戏搬砖是需要耗费大量的时间去玩游戏&#xff0c;熟悉游戏&#xff0c;利用自己的时间和技巧手段在游戏…

anyRTC 融合音视频能力底座:助力企业数字化转型

随着全球化的发展&#xff0c;产业竞争日益激烈。数字化转型和创新成为了企业提高竞争力、实现可持续发展的重要手段&#xff0c;面对产业结构调整、资源环境挑战、数字技术与创新带来的行业颠覆与机遇&#xff0c;企业需要进行数字化转型和创新以适应新环境和新时代的挑战。 …

Python爬虫404错误:解决方案总结

在进行网络爬虫开发的过程中&#xff0c;经常会遇到HTTP 404错误&#xff0c;即“Not Found”错误。这种错误通常表示所请求的资源不存在。对于爬虫开发者来说&#xff0c;处理这类错误是至关重要的&#xff0c;因为它们可能会导致爬虫无法正常工作。本文将探讨Python爬虫遇到4…

免费采集工具-免费的采集器

在当今数字信息爆炸的时代&#xff0c;人们对于获取并整理大量数据的需求愈发迫切。在这个背景下&#xff0c;免费采集工具应运而生&#xff0c;为用户提供了一种便捷的方式来获取并管理所需数据。在本文中&#xff0c;我们将深入探讨免费采集工具的优势、使用方法&#xff0c;…

什么软件可以去视频水印?分享3个超实用去水印工具

什么软件可以去视频水印&#xff1f;短视频已然成为了我们日常生活或工作的一部分&#xff0c;当我们遇到感兴趣的视频想保存发现无法保存&#xff0c;或者保存后留有水印&#xff0c;非常影响我们视频观看度和分享欲&#xff0c;为了解决这一问题&#xff0c;许多针对视频水印…

aPEAR包绘制功能富集网络图

本期教程 前言 今天学习aPEAR包&#xff0c;绘制KEGG和GO功能富集网络图&#xff0c;用起来还是比较方便的&#xff0c;直接将clusterProfiler富集结果进行绘制&#xff0c;对人类、动物等分析结果非常方便。对于模式植物&#xff0c;使用自己制作的GO或KEGG背景文件进行富集分…

Python Web包就业服务

还在等什么&#xff0c;加入我们&#xff0c;包就业

神经网络可视化——基于torchviz绘制模型的计算图

神经网络可视化——基于torchviz绘制模型的计算图 第一步、安装 graphviz 和 torchviz 库 第二步、编写代码生成计算图 第三步、安装graphviz软件 在深入理解深度学习模型时&#xff0c;可视化网络结构是一个非常有用的手段。今天介绍如何使用 torchviz 和 graphviz 来生成网…

lightdb-ignore_row_on_dupkey_index

LightDB 支持 ignore_row_on_dupkey_index hint LightDB 从23.4 开始支持oracle的 ignore_row_on_dupkey_index hint&#xff0c; 这个hint是用来忽略唯一键冲突的。类似与mysql的 insert ignore。 语法如下&#xff1a; 在LightDB中ignore_row_on_dupkey_index的效果等同于o…

智能井盖传感器怎么监测井盖倾斜?

城市道路上的井盖是常见的安全隐患&#xff0c;由于井盖质量不过关、安装不合理等原因导致的井盖位移或倾斜等事故&#xff0c;每年都处于不断增加的状态。为了减少此类案件的发生并维护社会治安&#xff0c;国家相关部门已经制定了多项政策法规对井盖进行统一监管。鼓励各个城…

基于vue框架积木网上商城的设计与实现

基于Vue框架积木网上商城的设计与实现 摘要&#xff1a;随着物资生活逐渐完善的今天&#xff0c;许多家庭越来越重视儿童的教育问题&#xff0c;儿童的智力发育问题渐渐受到大家的关注和重视&#xff0c;如何让儿童身心得到健康发展是很重要的社会问题。科学研究认为5-8岁是少…

P9240 [蓝桥杯 2023 省 B] 冶炼金属(比值问题)

数学分析&#xff1a; 1. max(最大比值) A/B 余数p&#xff08;p<B&#xff09; > Amax*Bp 反证&#xff1a;若max不为最大,则设maxn为最大比值 (maxn)*Bmax*Bn*Bp1 > A (n*Bp1 > p ,矛盾) 故max为最大比值 2.min(最小比值…