Flutter图表绘制与深度分析插件k_chart_plus_deeping的使用

发布于 1周前 作者 sinazl 来自 Flutter

Flutter图表绘制与深度分析插件k_chart_plus_deeping的使用

K Chart Plus Package Deeping

简体中文 | English

功能特性

  • 2024-08-01 更新:显示双量
  • 2024-07-18 更新:手势缩放功能|科学计数法支持
  • 2024-07-17 更新:添加EMA指标
  • 2024-07-16 更新:骨架屏功能

这个可能是Flutter中最好的K线图。支持拖拽、缩放、长按和滑动,并且易于使用。

Example1 Example2
Example1 Example2

安装

首先,在pubspec.yaml文件中添加k_chart_plus_deeping作为依赖项:

dependencies:
  k_chart_plus_deeping: ^1.0.0-tomo+2

如果不需要支持选择多个次要状态,可以使用以下配置:

flutter_k_chat_plus:
   git:
     url: https://github.com/sawwas/flutter_k_chat_plus
     ref: single # 分支名称

使用方法

数据处理

当你更改数据时,必须调用以下函数:

DataUtil.calculate(datas); // 此函数有一些可选参数:n 是 BOLL N 天收盘价。k 是 BOLL 参数。

使用K线图

KChartWidget(
    chartStyle, // 必填,用于样式设置
    chartColors, // 必填,用于样式设置
    datas, // 必填,数据必须是有序列表(历史->现在)
    mBaseHeight: 360, // 图表高度(不包含成交量和副图)
    isLine: isLine, // 决定是否为K线或分时图
    mainState: _mainState, // 决定主视图显示内容
    secondaryStateLi: _secondaryStateLi, // 决定副视图显示内容
    fixedLength: 2, // 显示的小数精度
    timeFormat: TimeFormat.YEAR_MONTH_DAY,
    onLoadMore: (bool a) {}, // 当数据滚动到末尾时调用。a 为 true 表示用户拉到了数据右侧末尾;a 为 false 表示用户拉到了数据左侧末尾。
    maDayList: [5, 10, 20], // MA 显示,此参数必须等于 DataUtil.calculate 的 maDayList
    volHidden: false, // 隐藏成交量
    showNowPrice: true, // 显示当前价格
    isOnDrag: (isDrag) {}, // 拖拽时为 true。拖拽时不加载数据。
    isTrendLine: false, // 设置为 true 后,可以通过长按并移动手指使用趋势线
    xFrontPadding: 100 // 前部填充
),

使用深度图

DepthChart(_bids, _asks, chartColors) // 注意:数据必须是有序列表

深色/浅色主题

ChartColor用于设置图表颜色。你需要根据主题配置灵活调整以确保UI效果。

如果你需要应用多主题,至少需要更改与文本、边框、网格和背景颜色相关的颜色。

late ThemeData themeData = Theme.of(context);
late ChartColors chartColors = ChartColors(
  bgColor: themeData.colorScheme.background,
  defaultTextColor: themeData.textTheme.labelMedium?.color ?? Colors.grey,
  gridColor: themeData.dividerColor,
  hCrossColor: themeData.textTheme.bodyMedium?.color ?? Colors.white,
  vCrossColor: themeData.disabledColor.withOpacity(.1),
  crossTextColor: themeData.textTheme.bodyMedium?.color ?? Colors.white,
  selectBorderColor: themeData.textTheme.bodyMedium?.color ?? Colors.black54,
  selectFillColor: themeData.colorScheme.background,
  infoWindowTitleColor: themeData.textTheme.labelMedium?.color ?? Colors.grey,
  infoWindowNormalColor: themeData.textTheme.bodyMedium?.color ?? Colors.white,
);

应用于K线图:

KChartWidget(
    data,
    ChartStyle(),
    ChartColors().init(), /// 自定义图表颜色
    chartTranslations: ChartTranslations(
        date: 'Date',
        open: 'Open',
        high: 'High',
        low: 'Low',
        close: 'Close',
        changeAmount: 'Change',
        change: 'Change%',
        amount: 'Amount',
        vol: 'Volume',
    ),
    mBaseHeight: 360,
    isTrendLine: false,
    mainState: mainState,
    secondaryStateLi: secondaryStates,
    fixedLength: 2,
    timeFormat: TimeFormat.YEAR_MONTH_DAY,
);

示例代码

以下是完整的示例代码,展示了如何在Flutter项目中使用k_chart_plus_deeping插件。

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:k_chart_plus_deeping/k_chart_plus.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: const MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key, this.title}) : super(key: key);

  final String? title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  List<KLineEntity>? datas;
  bool showLoading = true;
  bool _volHidden = false;
  MainState _mainState = MainState.mA;
  final List<SecondaryState> _secondaryStateLi = [];
  List<DepthEntity>? _bids, _asks;

  ChartStyle chartStyle = ChartStyle();
  ChartColors chartColors = ChartColors();

  @override
  void initState() {
    super.initState();
    getData('1day');
    rootBundle.loadString('assets/depth.json').then((result) {
      final parseJson = json.decode(result);
      final tick = parseJson['tick'] as Map<String, dynamic>;
      final List<DepthEntity> bids = (tick['bids'] as List<dynamic>)
          .map<DepthEntity>(
              (item) => DepthEntity(item[0] as double, item[1] as double))
          .toList();
      final List<DepthEntity> asks = (tick['asks'] as List<dynamic>)
          .map<DepthEntity>(
              (item) => DepthEntity(item[0] as double, item[1] as double))
          .toList();
      initDepth(bids, asks);
    });
  }

  void initDepth(List<DepthEntity>? bids, List<DepthEntity>? asks) {
    if (bids == null || asks == null || bids.isEmpty || asks.isEmpty) return;
    _bids = [];
    _asks = [];
    double amount = 0.0;
    bids.sort((left, right) => left.price.compareTo(right.price));
    for (var item in bids.reversed) {
      amount += item.vol;
      item.vol = amount;
      _bids!.insert(0, item);
    }

    amount = 0.0;
    asks.sort((left, right) => left.price.compareTo(right.price));
    for (var item in asks) {
      amount += item.vol;
      item.vol = amount;
      _asks!.add(item);
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView(
        shrinkWrap: true,
        children: [
          const SafeArea(bottom: false, child: SizedBox(height: 10)),
          Stack(children: [
            KChartWidget(
              datas,
              chartStyle,
              chartColors,
              mBaseHeight: 360,
              isTrendLine: false,
              mainState: _mainState,
              volHidden: _volHidden,
              secondaryStateLi: _secondaryStateLi.toSet(),
              fixedLength: 2,
              timeFormat: TimeFormat.yearMONTHDAY,
            ),
            if (showLoading)
              Container(
                width: double.infinity,
                height: 450,
                alignment: Alignment.center,
                child: const CircularProgressIndicator(),
              ),
          ]),
          _buildTitle(context, 'VOL'),
          buildVolButton(),
          _buildTitle(context, 'Main State'),
          buildMainButtons(),
          _buildTitle(context, 'Secondary State'),
          buildSecondButtons(),
          const SizedBox(height: 30),
          if (_bids != null && _asks != null)
            Container(
              color: Colors.white,
              height: 320,
              width: double.infinity,
              child: DepthChart(
                _bids!,
                _asks!,
                chartColors,
              ),
            )
        ],
      ),
    );
  }

  Widget _buildTitle(BuildContext context, String title) {
    return Padding(
      padding: const EdgeInsets.fromLTRB(16, 20, 12, 15),
      child: Text(
        title,
        style: Theme.of(context).textTheme.bodyMedium?.copyWith(
              fontWeight: FontWeight.w600,
            ),
      ),
    );
  }

  Widget buildVolButton() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Align(
        alignment: Alignment.centerLeft,
        child: _buildButton(
            context: context,
            title: 'VOL',
            isActive: !_volHidden,
            onPress: () {
              _volHidden = !_volHidden;
              setState(() {});
            }),
      ),
    );
  }

  Widget buildMainButtons() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Wrap(
        alignment: WrapAlignment.start,
        spacing: 10,
        runSpacing: 10,
        children: MainState.values.map((e) {
          return _buildButton(
            context: context,
            title: e.name,
            isActive: _mainState == e,
            onPress: () => _mainState = e,
          );
        }).toList(),
      ),
    );
  }

  Widget buildSecondButtons() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Wrap(
        alignment: WrapAlignment.start,
        spacing: 10,
        runSpacing: 5,
        children: SecondaryState.values.map((e) {
          bool isActive = _secondaryStateLi.contains(e);
          return _buildButton(
            context: context,
            title: e.name,
            isActive: _secondaryStateLi.contains(e),
            onPress: () {
              if (isActive) {
                _secondaryStateLi.remove(e);
              } else {
                _secondaryStateLi.add(e);
              }
            },
          );
        }).toList(),
      ),
    );
  }

  Widget _buildButton({
    required BuildContext context,
    required String title,
    required bool isActive,
    required Function onPress,
  }) {
    late Color? bgColor, txtColor;
    if (isActive) {
      bgColor = Theme.of(context).primaryColor.withOpacity(.15);
      txtColor = Theme.of(context).primaryColor;
    } else {
      bgColor = Colors.transparent;
      txtColor =
          Theme.of(context).textTheme.bodyMedium?.color?.withOpacity(.75);
    }
    return InkWell(
      onTap: () {
        onPress();
        setState(() {});
      },
      child: Container(
        decoration: BoxDecoration(
          color: bgColor,
          borderRadius: BorderRadius.circular(6),
        ),
        constraints: const BoxConstraints(minWidth: 60),
        padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
        child: Text(
          title,
          style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                color: txtColor,
              ),
          textAlign: TextAlign.center,
        ),
      ),
    );
  }

  void getData(String period) {
    final Future<String> future = getChatDataFromInternet(period);
    future.then((String result) {
      solveChatData(result);
    }).catchError((_) {
      showLoading = false;
      setState(() {});
      debugPrint('### datas error $_');
    });
  }

  Future<String> getChatDataFromInternet(String? period) async {
    var url =
        'https://api.huobi.br.com/market/history/kline?period=${period ?? '1day'}&size=300&symbol=btcusdt';
    late String result;
    final response = await http.get(Uri.parse(url));
    if (response.statusCode == 200) {
      result = response.body;
    } else {
      debugPrint('Failed getting IP address');
    }
    return result;
  }

  Future<String> getChatDataFromJson() async {
    return rootBundle.loadString('assets/chatData.json');
  }

  void solveChatData(String result) {
    final Map parseJson = json.decode(result) as Map<dynamic, dynamic>;
    final list = parseJson['data'] as List<dynamic>;
    datas = list
        .map((item) => KLineEntity.fromJson(item as Map<String, dynamic>))
        .toList()
        .reversed
        .toList()
        .cast<KLineEntity>();
    DataUtil.calculate(datas!);
    showLoading = false;
    setState(() {});
  }
}

致谢

感谢以下项目的贡献:

希望这些信息能帮助你在Flutter项目中成功使用k_chart_plus_deeping插件。如果你有任何问题或需要进一步的帮助,请随时提问!


更多关于Flutter图表绘制与深度分析插件k_chart_plus_deeping的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图表绘制与深度分析插件k_chart_plus_deeping的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,k_chart_plus_deeping 是一个用于绘制图表并进行深度分析的插件。虽然我不能直接提供该插件的官方实现细节或源代码(因为这通常受版权和分发协议的限制),但我可以展示一个基本的Flutter项目结构以及如何使用类似图表绘制库的通用方法。请注意,以下代码是一个假设性的示例,因为具体的 k_chart_plus_deeping API 细节并未公开。

首先,确保你已经在 pubspec.yaml 文件中添加了 k_chart_plus_deeping 依赖(假设它已经在Pub仓库中可用):

dependencies:
  flutter:
    sdk: flutter
  k_chart_plus_deeping: ^x.y.z  # 替换为实际的版本号

然后,运行 flutter pub get 来获取依赖。

接下来,是一个基本的Flutter页面示例,展示如何使用一个假设的图表绘制库来绘制图表:

import 'package:flutter/material.dart';
import 'package:k_chart_plus_deeping/k_chart_plus_deeping.dart';  // 假设这是库的导入路径

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Chart Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: ChartScreen(),
    );
  }
}

class ChartScreen extends StatefulWidget {
  @override
  _ChartScreenState createState() => _ChartScreenState();
}

class _ChartScreenState extends State<ChartScreen> {
  // 假设的数据源
  final List<Map<String, dynamic>> chartData = [
    {'x': 'A', 'y': 10},
    {'x': 'B', 'y': 20},
    {'x': 'C', 'y': 15},
    {'x': 'D', 'y': 25},
    {'x': 'E', 'y': 30},
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('K-Chart Plus Deeping Example'),
      ),
      body: Center(
        child: KChartPlusDeeping(
          data: chartData,  // 假设库接受这种格式的数据
          xAxisLabel: 'X Axis',
          yAxisLabel: 'Y Axis',
          title: 'Sample Chart',
          // 其他可能的配置选项
          // depthAnalysis: true, // 假设有一个深度分析的开关
          // 其他深度分析相关的配置...
        ),
      ),
    );
  }
}

// 假设的 KChartPlusDeeping 组件定义(实际使用时请参考官方文档)
class KChartPlusDeeping extends StatelessWidget {
  final List<Map<String, dynamic>> data;
  final String xAxisLabel;
  final String yAxisLabel;
  final String title;

  // 其他可能的配置参数...

  const KChartPlusDeeping({
    Key? key,
    required this.data,
    required this.xAxisLabel,
    required this.yAxisLabel,
    required this.title,
    // 其他配置参数默认值...
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // 这里应该包含实际的图表绘制逻辑
    // 但由于我们不知道具体的库实现,所以仅返回一个占位Text
    return Text('K-Chart Plus Deeping Chart Placeholder');
  }
}

注意

  1. 上述代码中的 KChartPlusDeeping 是一个假设的组件,用于说明如何在Flutter中使用一个图表绘制库。实际使用时,你需要参考 k_chart_plus_deeping 的官方文档和API。
  2. chartData 的格式和内容也是基于假设的,你需要根据 k_chart_plus_deeping 插件的实际要求来调整数据格式。
  3. 深度分析功能(如 depthAnalysis: true)和其他配置选项也是假设性的,你需要查阅官方文档以了解如何启用和配置这些功能。

建议直接访问 k_chart_plus_deeping 的官方仓库或文档页面,以获取最准确和最新的使用指南和示例代码。

回到顶部