我的编程空间,编程开发者的网络收藏夹
学习永远不晚

UI开源组件Flutter图表范围选择器使用详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

UI开源组件Flutter图表范围选择器使用详解

前言

最近有一个小需求:图表支持局部显示,如下底部的区域选择器支持

  • 左右拖动调节中间区域
  • 拖拽中间区域,可以进行移动
  • 图表数据根据中间区域的占比进行显示部分数据

这样当图表的数据量过大,不宜全部展示时,可选择的局部展示就是个不错的解决方案。由于一般的图表库没有提供该功能,这里自己通过绘制来实现以下,操作效果如下所示:

1. 使用 chart_range_selector

目前这个范围选择器已经发布到 pub 上了,名字是 chart_range_selector。大家可以通过依赖进行添加

dependencies:
  chart_range_selector: ^1.0.0

这个库本身是作为独立 UI 组件存在的,在拖拽过程中改变区域范围时,会触发回调。使用者可以通过监听来获取当前区域的范围。这里的区域起止是以分率的形式给出的,也就是最左侧是 0 最右侧是 1 。如下的区域范围是 0.26 ~ 0.72

ChartRangeSelector(
  height: 30,
  initStart: 0.4,
  initEnd: 0.6,
  onChartRangeChange: _onChartRangeChange,
),
void _onChartRangeChange(double start, double end) {
  print("start:$start, end:$end");
}

封装的组件名为: ChartRangeSelector ,提供了如下的一些配置参数:

配置项类型简述
initStartdouble范围启始值 0~1
initEnddouble范围终止值 0~1
heightdouble高度值
onChartRangeChangeOnChartRangeChange范围变化回调
bgStorkColorColor背景线条颜色
bgFillColorColor背景填充颜色
rangeColorColor区域颜色
rangeActiveColorColor区域激活颜色
dragBoxColorColor左右拖拽块颜色
dragBoxActiveColorColor左右拖拽块激活颜色

2. ChartRangeSelector 实现思路分析

这个组件整体上是通过 ChartRangeSelectorPainter 绘制出来的,其实这些图形都是挺规整的,绘制来说并不是什么难事。

重点在于事件的处理,拖拽不同的部位需要处理不同的逻辑,还涉及对拖拽部位的校验、高亮示意,对这块的整合还是需要一定的功力的。

代码中通过 RangeData 可监听对象为绘制提供必要的数据,其中 minGap 用于控制范围的最小值,保证范围不会过小。

另外定义了 OperationType 枚举表示操作,其中有四个元素,none 表示没有拖拽的普通状态;

dragHead 表示拖动起始块,dragTail 表示拖动终止块,dragZone 表示拖动范围区域。

enum OperationType{
  none,
  dragHead,
  dragTail,
  dragZone
}
class RangeData extends ChangeNotifier {
  double start;
  double end;
  double minGap;
  OperationType operationType=OperationType.none;
  RangeData({this.start = 0, this.end = 1,this.minGap=0.1});
  //暂略相关方法...
}

在组件构建中,通过 LayoutBuilder 获取组件的约束信息,从而获得约束区域宽度最大值,也就是说组件区域的宽度值由使用者自行约束,该组件并不强制指定。

使用 SizedBox 限定画板的高度,通过 CustomPaint 组件使用 ChartRangeSelectorPainter 进行绘制。

使用 GestureDetector 组件进行手势交互监听,这就是该组件整体上实现的思路。

3.核心代码实现分析

可以看出,这个组件的核心就是 绘制 + 手势交互 。其中绘制比较简单,就是根据 RangeData 数据和颜色配置画些方块而已,稍微困难一点的是对左右控制柄位置的计算。

另外,三个可拖拽物的激活状态是通过 RangeData#operationType 进行判断的。

也就是说所有问题的焦点都集中在 手势交互 中对 RangeData 数据的更新。如下是处理按下的逻辑,当触电横坐标左右 10 逻辑像素之内,表示激活头部。

如下 tag1 处通过 dragHead 方法更新 operationType 并触发通知,这样画板绘制时就会激活头部块,右侧和中间的激活同理。

---->[RangeData#dragHead]----
void dragHead(){
  operationType=OperationType.dragHead;
  notifyListeners();
}

void _onPanDown(DragDownDetails details, double width) {
  double start = width * rangeData.start;
  double x = details.localPosition.dx;
  double end = width * rangeData.end;
  if (x >= start - 10 && x <= end + 10) {
    if ((start - details.localPosition.dx).abs() < 10) {
      rangeData.dragHead(); // tag1
      return;
    }
    if ((end - details.localPosition.dx).abs() < 10) {
      rangeData.dragTail();
      return;
    }
    rangeData.dragZone();
  }
}

对于拖手势的处理,是比较复杂的。如下根据 operationType 进行不同的逻辑处理,比如当 dragHead 时,触发 RangeData#moveHead 方法移动 start 值。这里将具体地逻辑封装在 RangeData 类中。

可以使代码更加简洁明了,每个操作都有 bool 返回值用于校验区域也没有发生变化,比如拖拽到 0 时,继续拖拽是会触发事件的,此时返回 false,避免无意义的 onChartRangeChange 回调触发。

void _onUpdate(DragUpdateDetails details, double width) {
  bool changed = false;
  if (rangeData.operationType == OperationType.dragHead) {
    changed = rangeData.moveHead(details.delta.dx / width);
  }
  if (rangeData.operationType == OperationType.dragTail) {
    changed = rangeData.moveTail(details.delta.dx / width);
  }
  if (rangeData.operationType == OperationType.dragZone) {
    changed = rangeData.move(details.delta.dx / width);
  }
  if (changed) widget.onChartRangeChange.call(rangeData.start, rangeData.end);
}

如下是 RangeData#moveHead 的处理逻辑,_recordStart 用于记录起始值,如果移动后未改变,返回 false。表示不执行通知和触发回调。

---->[RangeData#moveHead]----
bool moveHead(double ds) {
  start += ds;
  start = start.clamp(0, end - minGap);
  if (start == _recordStart) return false;
  _recordStart = start;
  notifyListeners();
  return true;
}

4. 结合图表使用

下面是结合 charts_flutter 图标库实现的范围显示案例。其中核心点是 domainAxis 可以通过 NumericAxisSpec 来显示某个范围的数据,而 ChartRangeSelector 提供拽的交互操作来更新这个范围,可谓相辅相成。

class RangeChartDemo extends StatefulWidget {
  const RangeChartDemo({Key? key}) : super(key: key);
  @override
  State<RangeChartDemo> createState() => _RangeChartDemoState();
}
class _RangeChartDemoState extends State<RangeChartDemo> {
  List<ChartData> data = [];
  int start = 0;
  int end = 0;
  @override
  void initState() {
    super.initState();
    data = randomDayData(count: 96);
    start = 0;
    end = (0.8 * data.length).toInt();
  }
  Random random = Random();
  List<ChartData> randomDayData({int count = 1440}) {
    return List.generate(count, (index) {
      int value = 50 + random.nextInt(200);
      return ChartData(index, value);
    });
  }
  @override
  Widget build(BuildContext context) {
    List<charts.Series<ChartData, int>> seriesList = [
      charts.Series<ChartData, int>(
        id: 'something',
        colorFn: (_, __) => charts.MaterialPalette.blue.shadeDefault,
        domainFn: (ChartData sales, _) => sales.index,
        measureFn: (ChartData sales, _) => sales.value,
        data: data,
      )
    ];
    return Column(
      children: [
        Expanded(
          child: charts.LineChart(seriesList,
              animate: false,
              primaryMeasureAxis: const charts.NumericAxisSpec(
                  tickProviderSpec: charts.BasicNumericTickProviderSpec(desiredTickCount: 5),),
              domainAxis: charts.NumericAxisSpec(
                viewport: charts.NumericExtents(start, end),
              )),
        ),
        const SizedBox(
          height: 10,
        ),
        SizedBox(
          width: 400,
          child: ChartRangeSelector(
              height: 30,
              initEnd: 0.5,
              initStart: 0.3,
              onChartRangeChange: (start, end) {
                this.start = (start * data.length).toInt();
                this.end = (end * data.length).toInt();
                setState(() {});
              }),
        ),
      ],
    );
  }
}
class ChartData {
  final int index;
  final int value;
  ChartData(this.index, this.value);
}

以上就是UI 开源组件Flutter图表范围选择器使用详解的详细内容,更多关于Flutter图表范围选择器的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

UI开源组件Flutter图表范围选择器使用详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录