flutter自定义系列之简单的K线图绘制

news2024/12/24 21:44:57

上篇文章讲了flutter自定义的相关流程,

今天继续练习下flutter的自定义K线:

我们可以通过自定义Painter来实现一个简单的K线图界面:

  1. 创建一个自定义的Painter,用于绘制K线图:
import 'dart:ui';

import 'package:flutter/material.dart';

class KLinePainter extends CustomPainter {
  final List<dynamic> data;
  final double itemWidth;
  final double scaleFactor;

  KLinePainter({required this.data, required this.itemWidth, required this.scaleFactor});

  @override
  void paint(Canvas canvas, Size size) {
    // 设置画笔
    final linePaint = Paint()
      ..color = Colors.grey
      ..strokeWidth = 0.5;

    final textPainter = TextPainter(textDirection: TextDirection.ltr, textAlign: TextAlign.left);

    // 计算价格范围和最大成交量
    num highestPrice = double.minPositive;
    num lowestPrice = double.maxFinite;
    num highestVolume = double.minPositive;

    for (var item in data) {
      if (item['high'] > highestPrice) {
        highestPrice = item['high'];
      }
      if (item['low'] < lowestPrice) {
        lowestPrice = item['low'];
      }
      if (item['vol'] > highestVolume) {
        highestVolume = item['vol'];
      }
    }

    // 计算价格和成交量的缩放比例
    double priceScale = (size.height - 20) / (highestPrice - lowestPrice);
    double volumeScale = size.height * 0.2 / highestVolume;

    // 绘制K线图
    for (int i = 0; i < data.length; i++) {
      var item = data[i];

      double open = (item['open'] - lowestPrice) * priceScale;
      double close = (item['close'] - lowestPrice) * priceScale;
      double high = (item['high'] - lowestPrice) * priceScale;
      double low = (item['low'] - lowestPrice) * priceScale;
      double vol = item['vol'] * volumeScale;

      // 设置画笔颜色
      linePaint.color = close >= open ? Colors.green : Colors.red;

      // 绘制实体
      double halfWidth = itemWidth * scaleFactor / 2;
      double centerX = i * itemWidth * scaleFactor + halfWidth;
      canvas.drawRect(
        Rect.fromCenter(
          center: Offset(centerX, size.height - (open + close) / 2 - 10),
          width: itemWidth * scaleFactor,
          height: (open - close).abs(),
        ),
        linePaint,
      );

      // 绘制上下影线
      canvas.drawLine(Offset(centerX, size.height - high - 10), Offset(centerX, size.height - low - 10), linePaint);
    }
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return true;
  }
}
  1. 创建一个StatefulWidget,用于处理缩放和滚动:
class KLineChart extends StatefulWidget {
  final List<dynamic> data;

  KLineChart({required this.data});

  @override
  _KLineChartState createState() => _KLineChartState();
}

class _KLineChartState extends State<KLineChart> {
  double _scaleFactor = 1.0;
  double _itemWidth = 10.0;
  late double _baseScaleFactor;

  @override
  void initState() {
    super.initState();
    _baseScaleFactor = _scaleFactor;
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onScaleStart: (details) {
        _baseScaleFactor = _scaleFactor;
      },
      onScaleUpdate: (details) {
        setState(() {
          // 修改这里,减小缩放速度
          _scaleFactor = _baseScaleFactor * (1.0 + (details.scale - 1.0) / 2);
        });
      },
      child: SingleChildScrollView(
        scrollDirection: Axis.horizontal,
        child: CustomPaint(
          size: Size(widget.data.length * _itemWidth * _scaleFactor, MediaQuery.of(context).size.height),
          painter: KLinePainter(data: widget.data, itemWidth: _itemWidth, scaleFactor: _scaleFactor),
        ),
      ),
    );
  }
}
  1. 在需要使用K线图的地方,调用KLineChart组件并传入K线数据:
List<dynamic> kLineData = [
  // 你的K线数据
];

KLineChart(data: kLineData);

这样,你就可以实现一个简单的可左右滑动、放大缩小的K线界面。

这里我们看看实现的效果:
我先模拟了一些K线数据:

List<dynamic> generateKLineData(int count) {
  List<dynamic> data = [];
  Random random = Random();
  double open = 100.0;
  double close, high, low;
  num volume;

  for (int i = 0; i < count; i++) {
    close = open + random.nextDouble() * 20 - 10;
    high = max(open, close) + random.nextDouble() * 5;
    low = min(open, close) - random.nextDouble() * 5;
    volume = random.nextInt(10000) + 1000;

    data.add({
      'open': open,
      'close': close,
      'high': high,
      'low': low,
      'vol': volume,
    });

    open = close;
  }

  return data;
}

之后直接引用:

SliverToBoxAdapter(
  child: SizedBox(
    height: 300.w,
    child: KLineChart(data: generateKLineData(100)),
  ),
)

运行后如下效果:

图片.png

可左右滑动,可伸缩,仅包含基本功能,真正的需要分时图,各种的指标,甚至是自定义的指标绘制等等,你可以根据需求进一步完善和优化。

技术人日拱一卒,共勉!

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

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

相关文章

聊聊多线程

摘要 开发过程中&#xff0c;总会遇到一些并发安全问题。本文总结出常用的数据结构哪些是安全的&#xff0c;哪些是不安全的以及他们为什么是不安全。 java中sychronize锁的原理&#xff1a; 常见的数据结构 类型 数据结构是否安全ArrayList数组 不安全HashMap数…

Mocha Pro:AdjustTrack 模块

跟踪时由于缺乏细节或有障碍物阻挡&#xff0c;跟踪点发生了漂移&#xff0c;或者一个或多个跟踪点可能会离开画面&#xff0c;此时可考虑使用 AdjustTrack &#xff08;调整跟踪&#xff09;模块手动设置关键帧来获得更精准的跟踪数据。 尤其是当要利用表面 Surface区域进行插…

随机数组归并问题

1 问题 生成两个任意的随机数组&#xff0c;并将这两个数组按照数字大小按顺序归并到一个新数组中。 2 方法 思路&#xff1a;定义三个数组&#xff0c;两个数组自己输入值&#xff0c;第三个数组用来作归并后的数组&#xff0c;先将两个数组的值全部赋给第三个数组&#xff0c…

极简主义的远程文件浏览器Mikochi

什么是 Mikochi &#xff1f; Mikochi 是一个远程文件浏览器&#xff0c;用于自托管服务器 / NAS。它允许您浏览远程文件夹、上传文件、删除、重命名、下载和流式传输文件到 VLC/mpv。它带有一个由 JavaScript/Preact 提供支持的 Web 界面&#xff0c;以及一个内置于 Go/Gin 中…

ChatGPT 教我用 200 行代码写一个简版 Vue 框架 - OpenTiny

AI 是未来最好的老师 最近&#xff0c;我正在准备一份关于 Vue 基础的学习材料。期间我突发奇想&#xff1a;能否利用现在热门的 ChatGPT 帮我创建学习内容&#xff1f;其实 Vue 本身不难学&#xff0c;特别是基础用法&#xff0c;但是&#xff0c;如果你想深入掌握 Vue&#…

数据挖掘(7.1)--数据仓库

目录 引言 一、数据库 1.简介 2.数据库管理系统(DBMS) 二、数据仓库 数据仓库特征 数据仓库作用 数据仓库和DBMS对比 分离数据仓库和数据库 引言 数据仓库的历史可以追溯到20世纪60年代&#xff0c;当时计算机领域的主要工作是创建运行在主文件上的单个应用&#xff0…

LaravelPHP笔记-响应头去掉(隐藏)X-Powered-By

最近想搞个小项目&#xff0c;后端先用PHP&#xff0c;框架是Laravel但http响应头如下&#xff1a; 头带有X-Powered-By: PHP/7.3.33&#xff0c;这样很不安全&#xff0c;应该要隐藏&#xff0c;查了下百度。都是一个抄一个。 在代码中添加&#xff1a; header_remove(x-pow…

【几分醉意赠书活动 - 02期】 | 《前端系列丛书》

个人主页&#xff1a; 几分醉意的CSDN博客主页_传送门 个人主页&#xff1a; 陈老板的CSDN博客主页_传送门 赠书活动 | 第二期 本期好书推荐&#xff1a;《前端系列丛书》 粉丝福利&#xff1a;书籍赠送&#xff1a;共计送出30本 参与方式&#xff1a;关注公众号&#xff1a;码…

Flutter控件封装之轮播图Banner

Flutter中实现轮播图的方式有很多种&#xff0c;比如使用三方flutter_swiper&#xff0c;card_swiper等等&#xff0c;使用这些三方&#xff0c;可以很快很方便的实现一个轮播图展示&#xff0c;基本上也能满足我们日常的开发需求&#xff0c;如果说&#xff0c;想要一些定制化…

CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)

原文网址&#xff1a;CloudFlare系列--使用第三方来自定义CDN的IP(笨牛简洁版)_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍CloudFlare的CDN如何自定义第三方IP。 概述 CloudFlare官网接入域名的方式只能是 NS 接入&#xff0c;这样默认DNS服务器只能改为CloudFlare的D…

第3章 需求分析

第3章 需求分析 3.1 需求分析任务 3.1.1 确定对系统的综合要求 1. 功能需求 通过需求分析应该划分出必须完成的所有功能。 2. 性能需求 性能需求指定系统必须满足的定时约束或容量约束 3. 可靠性和可用性需求 可靠性需求定量地指定系统的可靠性 可用性与可靠性密切相关&…

北京某金融公司面试题,精选10道讲解!

你好&#xff0c;我是田哥 面试造火箭工作拧螺丝&#xff0c;最近一位朋友在面试中被问到各种各样的分布式微服务的面试题&#xff0c;也回答上来了。可是&#xff0c;等正式入职后&#xff0c;发现这家公司居然全部是使用单体项目&#xff0c;完全没有分布式微服务的东东&…

个人PC机使用网线与树莓派进行连接

目录 0. 前言1. 查看网络状况2. 设置网络共享3. 获取树莓派的IP 0. 前言 你需要准备一个树莓派4B&#xff0c;自己的电脑&#xff0c;以及一根超五类网线 操作系统&#xff1a;Windows10 专业版、Raspbian OS 开发环境&#xff1a;树莓派4B 1. 查看网络状况 windows控制台…

今年程序员去大厂面试的必备条件:985或211计算机专业,上家公司是大厂,毕业3年且30岁以下,之前产品qps在一万以上!...

什么样的程序员能拿到大厂的面试入场券&#xff1f; 一位网友总结&#xff0c;今年程序员想约到一二三线公司面试需要同时满足以下条件&#xff0c;缺一不可&#xff1a; 1.985或者211硕士&#xff0c;计算机专业&#xff1b; 2.上家公司是大厂&#xff1b; 3.毕业3年以上且年龄…

chatgpt赋能python:Python中的变量定义

Python中的变量定义 在Python中&#xff0c;变量是一种用来存储数据的容器。它们允许程序员为数据分配一个名称&#xff0c;并将该名称与特定的值关联起来。Python语言的灵活性和易用性使得变量定义变得极为简单。 定义变量的基本语法 在Python中&#xff0c;定义变量的语法…

pikachu靶场漏洞演练(更新中)

文章目录 一、XSS(Cross-Site Scripting)1.XSS概述2.漏洞危害3.常用payloadb.反射型XSS&#xff08;post&#xff09;c.存储型XSSd.DOM型XSSe.DOM型XSS-X 一、XSS(Cross-Site Scripting) 1.XSS概述 XSS中文叫做跨站脚本攻击&#xff08;Cross-site scripting&#xff09;&…

BitSet—位图

BitSet &#x1f50e;概念&#x1f50e;位图的模拟实现set()get()reSet()getUsedSize()完整代码 &#x1f50e;利用位图进行排序&#x1f50e;结尾 &#x1f50e;概念 位图 用某一位表示存储的状态 位图的适用场景 海量数据数据为自然数(≥ 0)数据不重复 举个栗子&#x1f3…

内网隧道代理技术(二)之LCX端口转发

LCX端口转发 LCX介绍 LCX是一款端口转发工具&#xff0c;分为Windows版和Linux版&#xff0c;Linux版本为PortMap。LCX有端口映射和端口转发两大功能&#xff0c;例如当目标的3389端口只对内开放而不对外开放时&#xff0c;可以使用端口映射将3389端口映射到目标的其他端口使…

计算两个向量的外积numpy.outer()

【小白从小学Python、C、Java】 【等级考试500强双证书考研】 【Python-数据分析】 计算两个向量的外积 numpy.outer() 以下说法正确的是&#xff1a; import numpy as np a np.array([1,2]) print("【显示】a ",a) b np.array([3,4,5]) print("【显示】b &q…

SpringBoot进阶-SpringBoot如何实现配置文件脱敏

目录 参考一、概述二、实现1、引入pom2、在配置文件中添加密钥3、生成加密之后的数据4、将加密之后的数据添加到配置文件中 三、踩坑Encryption raised an exception. A possible cause is you are using strong encryption algorithms and you have not installed the Java Cr…