Flutter 实战:仿 GitHub 贡献度热力图

在这篇文章中,我们将深入探索如何在Flutter中创建一个类似GitHub提交热力图的自定义视图。这种热力图是数据可视化的一种流行形式,用于显示例如代码提交频率等时间序列数据的密集程度。通过Flutter,我们可以使用CustomPainterStatelessWidget来实现这种复杂的视图。下面,我们将一步步解析这个过程,展示如何构建这个有趣且实用的组件。

源码仓库:github.com/sinyu1012/f...

1. 组件概览

我们的旅程从定义HeatMap这个StatelessWidget开始。这个组件接受多种参数,例如data(一个DateTimeint的映射),表示数据点,以及colors,一个颜色数组,用来可视化不同数据密度。

dart 复制代码
class HeatMap extends StatelessWidget {
  //...[参数定义]...

  @override
  Widget build(BuildContext context) {
    //...[构建逻辑]...
  }
}

2. 组件布局和自定义

HeatMap组件利用AspectRatio来维持热图的宽高比,并使用CustomPaint来绘制热图。CustomPaint的画笔是HeatMapPainter对象,它是我们的自定义画笔。

dart 复制代码
return AspectRatio(
  aspectRatio: aspectRatio,
  child: CustomPaint(
    painter: HeatMapPainter(
      data: data,
      colors: colors ??
          [
            Colors.green.shade200,
            Colors.green.shade400,
            Colors.green.shade600,
            Colors.green.shade800,
          ],
      strokeColor: strokeColor ?? Colors.red.shade100,
      textStyle: textStyle ??
          TextStyle(
            color: Colors.black.withOpacity(0.9),
            fontSize: 12,
          ),
      itemPadding: itemPadding,
      itemSize: itemSize,
    ),
  ),
);

3. 自定义绘制逻辑

接下来,我们定义了HeatMapPainter类,这个类是绘制热图的核心。它需要数据集、颜色数组等参数来绘制每个数据点。

dart 复制代码
class HeatMapPainter extends CustomPainter {
    //...[绘制器的参数]...

    @override
    void paint(Canvas canvas, Size size) {
      var paint = Paint();
      int cols = _calculateColumns(size.width);
      hasDrawnMonth = List.filled(cols, false);
      int totalCount = cols * rows;

      double heatMapWidth = _calculateHeatMapWidth(cols);
      double startX = _calculateStartX(size.width, heatMapWidth);

      Paint strokePaint = createStrokePaint();

      for (int i = 0; i < rows * cols; i++) {
        var dateAtIndex = _calculateDateForIndex(cols, i);
        var value = data[dateAtIndex] ?? 0;

        paint.color = _getColorForValue(value);
        _drawCell(canvas, paint, i, startX, strokePaint, dateAtIndex);
      }
    }
}

4. 绘制单元格

paint方法中,我们遍历并为热图创建每个单元格。每个单元格的颜色根据其数据值决定。特别的,如果日期是今天,会有一个特殊的边框样式。

dart 复制代码
void _drawCell(Canvas canvas, Paint paint, int index, double startX,
    Paint strokePaint, DateTime dateAtIndex) {
  var col = index ~/ rows;
  var row = index % rows;
  final rect = _calculateCellRect(startX, col, row);

  canvas.drawRRect(
      RRect.fromRectAndRadius(rect, const Radius.circular(4)), paint);

  if (dateAtIndex.isToday) {
    canvas.drawRRect(
        RRect.fromRectAndRadius(rect, const Radius.circular(4)), strokePaint);
  }

  if (dateAtIndex.day == 1 && !hasDrawnMonth[col]) {
    hasDrawnMonth[col] = true;
    _drawMonthText(canvas, dateAtIndex, col, startX);
  }
}

5. 处理日期和文本

此外,我们还处理了日期的计算和月份文本的绘制,这对于用户理解热图的时间跨度非常重要。

dart 复制代码
void _drawMonthText(Canvas canvas, DateTime date, int col, double startX) {
  String monthText = intl.DateFormat('MMM').format(date);

  TextPainter textPainter = TextPainter(
    text: TextSpan(text: monthText, style: textStyle),
    textDirection: TextDirection.ltr,
  );

  textPainter.layout();

  double xPosition = _calculateTextXPosition(col, startX, textPainter.width);
  Offset textPosition = Offset(
      xPosition, (rows * itemSize) + (rows * itemPadding) + itemPadding);

  textPainter.paint(canvas, textPosition);
}

6. 总结

通过上述的步骤,我们展示了如何在Flutter中创建一个类似GitHub提交热力图的自定义视图。这种视图在展示用户活动、项目进度或任何其他时间序列数据方面非常有用。利用Flutter的CustomPainter,我们可以完全控制视图的外观和行为,创造出既独特又功能强大的数据可视化工具。

源码仓库:github.com/sinyu1012/f...

相关推荐
Lanren的编程日记5 小时前
Flutter鸿蒙应用开发:数据统计与分析功能集成实战
flutter·华为·harmonyos
于慨10 小时前
mac安装flutter
javascript·flutter·macos
2601_9495936511 小时前
Flutter_OpenHarmony_三方库_image_picker图片视频采集适配详解
flutter·音视频
2601_9495936514 小时前
Flutter_OpenHarmony_三方库_fluttertoast消息提示适配详解
flutter
seabirdssss14 小时前
Flutter 开发环境配置
android·windows·flutter·adb
2601_9495936515 小时前
Flutter_OpenHarmony_三方库_webview_flutter网页内容嵌入与交互适配详解
flutter·harmonyos
tangweiguo0305198715 小时前
Flutter 分页缓存实战:基于 Riverpod 的 SWR 策略实现
flutter
Ww.xh17 小时前
鸿蒙Flutter混合开发实战:跨平台UI无缝集成
flutter·华为·harmonyos
SoulRed17 小时前
Android Studio 调试flutter gradle的问题
android·flutter·android studio
blanks202017 小时前
为 Zed 编辑器 添加 flutter dart snippets
前端·flutter