主页 > 其他  > 

Flutter:K线图

Flutter:K线图

使用插件:k_chart,

k_chart: ^0.7.1

首页进入详情需要传对应币种的symbol:BTCUSDT

.onTap((){ Get.toNamed('/k_chart_page', arguments: {'symbol': item.symbol}); }),

下面主要记录下K线的实现过程

1、定义model模型,前端需要传给后台的

{ symbol: BTCUSDT, // 币种 interval: 1m, // 时间 limit: 500, // 条数 } import 'package:xiaoshukeji/common/index.dart'; class HomeKlineReq { String? symbol; String? interval; int? limit; HomeKlineReq({this.symbol, this.interval, this.limit}); factory HomeKlineReq.fromJson(Map<String, dynamic> json) => HomeKlineReq( symbol: DataUtils.toStr(json['symbol']), interval: DataUtils.toStr(json['interval']), limit: DataUtils.toInt(json['limit']), ); Map<String, dynamic> toJson() => { 'symbol': symbol, 'interval': interval, 'limit': limit, }; }

2、后台返回数据格式处理,数据来源于火币网

[ [ 1499040000000, // 开盘时间 "0.01634790", // 开盘价 "0.80000000", // 最高价 "0.01575800", // 最低价 "0.01577100", // 收盘价(当前K线未结束的即为最新价) "148976.11427815", // 成交量 1499644799999, // 收盘时间 "2434.19055334", // 成交额 308, // 成交笔数 "1756.87402397", // 主动买入成交量 "28.46694368", // 主动买入成交额 "17928899.62484339" // 请忽略该参数 ] ]

这个格式并不能直接在k_chart中使用,需要自己处理下数据 在flutter_k_chart演示demo中,找到了官方的数据默认格式

// kline_1M.json id=时间戳 [{ "amount": 46197.237447628898264056, "open": 7121.890000000000000000, "close": 7102.900000000000000000, "high": 7380.000000000000000000, "id": 1574438400, "count": 296348, "low": 7094.690000000000000000, "vol": 333578883.234124950380397343870000000000000000 }, { "id": 1574352000, "open": 7628.590000000000000000, "close": 7121.440000000000000000, "high": 7725.090000000000000000, "low": 6790.000000000000000000, "vol": 664521165.622325248991851131, "amount": 91327.402878725150140416, "count": 627969 } ]

按照这个格式制作model

import 'package:xiaoshukeji/common/index.dart'; class KlineData { final double amount; final double open; final double close; final double high; final int time; final int count; final double low; final double vol; KlineData({ required this.amount, required this.open, required this.close, required this.high, required this.time, required this.count, required this.low, required this.vol, }); factory KlineData.fromJson(List<dynamic> json) { return KlineData( amount: DataUtils.toDouble(json[5]) ?? 0, // 成交量 open: DataUtils.toDouble(json[1]) ?? 0, // 开盘价 close: DataUtils.toDouble(json[4]) ?? 0, // 收盘价 high: DataUtils.toDouble(json[2]) ?? 0, // 最高价 time: DataUtils.toInt(json[0]) ?? 0, // 时间 count: DataUtils.toInt(json[8]) ?? 0, // 笔数 low: DataUtils.toDouble(json[3]) ?? 0, // 最低价 vol: DataUtils.toDouble(json[7]) ?? 0, // 24小时成交量 ); } Map<String, dynamic> toJson() => { 'amount': amount, 'open': open, 'close': close, 'high': high, 'time': time, 'count': count, 'low': low, 'vol': vol, }; } // 接口请求,获取K线数据 static Future<List<KlineData>> klineData(HomeKlineReq data) async { var res = await WPHttpService.to.get( '/api/markets/api/v3/klines', params: data.toJson(), ); var klineData = res.data; if (klineData != null && klineData is List && klineData.isNotEmpty) { return klineData.map((e) => KlineData.fromJson(e)).toList(); } return []; }

3、controller控制器获取数据

import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:k_chart/flutter_k_chart.dart'; import 'dart:async'; import 'package:xiaoshukeji/common/index.dart'; class KChartController extends GetxController { KChartController(); // 币种 String symbol = ''; // 头部24小时数据 HomeKlineHeadPriceModel headPrice = HomeKlineHeadPriceModel(); // K线数据 List<KLineEntity> datas = []; // 初始化为空列表 // 主图指标 MainState mainState = MainState.MA; // 副图指标 SecondaryState secondaryState = SecondaryState.MACD; // 当前选中的时间周期 String period = '1m'; // 默认1分钟 // 定时器 Timer? _timer; // // 初始化数据 @override void onInit() { super.onInit(); _initData(); _startTimer(); } @override void onClose() { _timer?.cancel(); super.onClose(); } // // 初始化数据 void _initData() async { symbol = Get.arguments?['symbol'] ?? ''; fetchKLineHeadPrice(); fetchKLineData(); } // 获取K线头部24小时数据 Future<void> fetchKLineHeadPrice() async { headPrice = await HomeApi.klineHeadPrice(symbol); update(["k_chart"]); } // 获取K线数据 Future<void> fetchKLineData() async { try { // 根据不同周期读取不同的json文件 List<KlineData> klineData = await HomeApi.klineData(HomeKlineReq(symbol: symbol, interval: period, limit: 1000)); datas = klineData.map((item) { double open = item.open; double close = item.close; double change = close - open; // change: 价格变化,通常是收盘价与开盘价的差值 double ratio = (change / open) * 100; // ratio: 价格变化百分比 return KLineEntity.fromCustom( time: item.time, open: open, high: item.high, low: item.low, close: close, vol: item.vol, amount: item.amount, change: change, ratio: ratio, ); }).toList(); DataUtil.calculate(datas); update(["k_chart"]); } catch (e) { print('获取K线数据失败: $e'); } } // 启动定时器 void _startTimer() { _timer = Timer.periodic(const Duration(seconds: 5), (timer) { if (datas.isEmpty) return; fetchKLineData(); }); } // 切换时间周期 void changePeriod(String newPeriod) { period = newPeriod; print('切换周期: $period'); fetchKLineData(); // 重新获取对应周期的数据 update(["k_chart"]); } // 切换主图指标 void changeMainState(MainState state) { mainState = state; update(["k_chart"]); } // 切换副图指标 void changeSecondaryState(SecondaryState state) { secondaryState = state; update(["k_chart"]); } // 获取价格变化颜色 Color getPriceChangeColor(String? percent) { if (percent == null) return AppTheme.textColorlv; // 转换为 double 进行比较 try { double value = double.parse(percent); return value < 0 ? const Color(0xffcd3746) : const Color(0xff01b56b); } catch (e) { return AppTheme.textColorlv; // 转换失败返回默认颜色 } } }

4、view视图

import 'package:ducafe_ui_core/ducafe_ui_core.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:k_chart/k_chart_widget.dart'; import 'package:tdesign_flutter/tdesign_flutter.dart'; import 'package:xiaoshukeji/common/index.dart'; import 'package:k_chart/flutter_k_chart.dart'; import 'index.dart'; class KChartPage extends GetView<KChartController> { const KChartPage({super.key}); // 头部数据 Widget _buildHeader() { return <Widget>[ <Widget>[ TextWidget.body('${controller.headPrice.lastPrice}',size:30.sp,color: AppTheme.textColorfff,weight: FontWeight.w600,), SizedBox(height: 15.w), <Widget>[ TextWidget.body('${controller.headPrice.priceChangePercent}%',size:26.sp,color: AppTheme.textColorfff,weight: FontWeight.w600,), ].toRow().paddingHorizontal(30.w).card( color: controller.getPriceChangeColor(controller.headPrice.priceChangePercent), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10.w)), ).height(40.w) ].toColumn(mainAxisAlignment: MainAxisAlignment.center), <Widget>[ TextWidget.body('24h量 ${controller.headPrice.volume}',size:24.sp,color: AppTheme.textColorfff,), SizedBox(height: 10.w), TextWidget.body('24h高 ${controller.headPrice.highPrice}',size:24.sp,color: AppTheme.textColorfff,), SizedBox(height: 10.w), TextWidget.body('24h低 ${controller.headPrice.lowPrice}',size:24.sp,color: AppTheme.textColorfff,), ].toColumn(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.start), ].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween).paddingHorizontal(30.w).card(color: AppTheme.blockBgColor).tight(width: 690.w, height: 160.w); } // 时间选择 Widget _buildPeriodButtons() { return <Widget>[ _buildPeriodButton('1m', '1分钟'), _buildPeriodButton('5m', '5分钟'), _buildPeriodButton('15m', '15分钟'), _buildPeriodButton('30m', '30分钟'), _buildPeriodButton('1h', '1小时'), _buildPeriodButton('1d', '日线'), ].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween).paddingHorizontal(30.w).height(90.w); } Widget _buildPeriodButton(String period, String label) { bool isSelected = controller.period == period; return TextWidget.body(label, color: isSelected ? AppTheme.primaryYellow : AppTheme.textColor646).onTap(() { controller.changePeriod(period); }); } // 指标切换按钮 Widget _buildIndicatorButtons() { return <Widget>[ TextWidget.body('MA', color: controller.mainState == MainState.MA ? AppTheme.primaryYellow : AppTheme.textColor646).onTap(() { controller.changeMainState(MainState.MA); }), SizedBox(width: 20.w), TextWidget.body('BOLL',color: controller.mainState == MainState.BOLL ? AppTheme.primaryYellow : AppTheme.textColor646).onTap(() { controller.changeMainState(MainState.BOLL); }), SizedBox(width: 100.w), TextWidget.body('MACD',color: controller.secondaryState == SecondaryState.MACD ? AppTheme.primaryYellow : AppTheme.textColor646).onTap(() { controller.changeSecondaryState(SecondaryState.MACD); }), SizedBox(width: 20.w), TextWidget.body('KDJ',color: controller.secondaryState == SecondaryState.KDJ ? AppTheme.primaryYellow : AppTheme.textColor646 ).onTap(() { controller.changeSecondaryState(SecondaryState.KDJ); }), SizedBox(width: 20.w), TextWidget.body('RSI',color: controller.secondaryState == SecondaryState.RSI ? AppTheme.primaryYellow : AppTheme.textColor646).onTap(() { controller.changeSecondaryState(SecondaryState.RSI); }), ].toRow(mainAxisAlignment: MainAxisAlignment.spaceBetween).paddingHorizontal(60.w).card(color: AppTheme.blockBgColor).height(80.w); } @override Widget build(BuildContext context) { return GetBuilder<KChartController>( init: KChartController(), id: "k_chart", builder: (_) { return Scaffold( backgroundColor: AppTheme.pageBgColor, appBar: TDNavBar( height: 45, title: controller.symbol, titleColor: AppTheme.textColorfff, titleFontWeight: FontWeight.w600, backgroundColor: AppTheme.navBarBgColor, screenAdaptation: true, useDefaultBack: true, ), body: Column( children: [ SizedBox(height: 30.w), _buildHeader(), _buildPeriodButtons(), SizedBox( width: 750.w, child: KChartWidget( controller.datas, ChartStyle(), ChartColors(), isLine: false, isTapShowInfoDialog:true, // 单击显示详细信息 mainState: controller.mainState, secondaryState: controller.secondaryState, volHidden: true, // 隐藏量 isTrendLine: false, // 显示趋势线 ), ).expanded(), _buildIndicatorButtons(), ], ), ); }, ); } }
标签:

Flutter:K线图由讯客互联其他栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Flutter:K线图