美文网首页
用CustomPaint自定义ChartView

用CustomPaint自定义ChartView

作者: 大丸蛇 | 来源:发表于2020-11-03 15:31 被阅读0次

    CustomPaint用途

    CustomPaint是flutter提供实现自定义view的类。作为SingleChildRenderObjectWidget的子类最多可以有一个子widget。

    const CustomPaint({
        Key? key,
        this.painter,
        this.foregroundPainter,
        this.size = Size.zero,
        this.isComplex = false,
        this.willChange = false,
        Widget? child,
      }) : assert(size != null),
           assert(isComplex != null),
           assert(willChange != null),
           assert(painter != null || foregroundPainter != null || (!isComplex && !willChange)),
           super(key: key, child: child);
    
    

    通过构造方法可以看到CustomPaint可以设置背景和前景的绘制,可以通过size设置大小,通过isComplex开启复杂界面缓存的模式。willChange是和isComplex配合使用的,当启用缓存时,该属性代表在下一帧中绘制是否会改变。

    其中我们要关注的是 'painter' ,这里是实现view渲染部分的部分。
    painter 的类型是CustomPainter ,是一个抽象类,提供了一些接口用于view的绘画渲染。我们要实现的也是这个类,通过实现CustomPainter后让底层的实现类替我们画出完整的view。

    在画条形图中,我们需要关注和实现的只有两个方法

    • void paint(Canvas canvas, Size size)
    • bool shouldRepaint(CustomPainter oldDelegate)

    paint 方法决定我们绘制什么 shouldRepaint的返回值决定这个view是否会被重新绘制。

    绘制view的思路

    view的拆分

    把view拆成这几个部分有助于我们理解和构思整个view。
    这个日期统计条形图的绘制总体分为这几个部分。

    • 柱形指示条
    • 白灰色相间背景
    • 右侧组限的文言
    • 上方的跳出的popup
    • 底下组的文言和虚线

    view的绘画

    • 柱形指示条
      主要要用canvas.drawRRect 来画出带有圆角的柱形
    void drawRRect(RRect rrect, Paint paint)
    
    • 白灰色相间背景
      同上不过用的是canvas.drawRect画矩形白灰相间的背景

    • 右侧组限的文言 和底下组文言用paragraphBuilder来绘制
      由于原生绘制文言的方式较为麻烦。底下用extension实现了对 Canvas的拓展。

    extension Draw on ui.Canvas {
      void drawText(
        Offset offset,
        ui.TextStyle textStyle,
        String value, {
        TextAlign textAlign = TextAlign.start,
        double fontSize = 13,
        double maxWidth = 100.0,
        FontWeight fontWeight = FontWeight.normal,
      }) {
        var paragraphBuilder = ui.ParagraphBuilder(
          ui.ParagraphStyle(
              textAlign: textAlign, fontSize: fontSize, fontWeight: fontWeight),
        )..pushStyle(textStyle);
        paragraphBuilder.addText(value);
        var pc = ui.ParagraphConstraints(width: maxWidth);
        var textParagraph = paragraphBuilder.build()..layout(pc);
        drawParagraph(textParagraph, offset);
      }
    }
    
    • 画柱形后面的虚线

    由于虚线在flutter没有api可以实现,我们可以隔着画一定距离画实线的方式画出虚线

      void _drawVerticalDashLine(ui.Canvas canvas, int key, String value) {
        _linePaint.color = BeautyColors.colorChartDash;
        double dashLineHeight;
        if (value != null && value.isNotEmpty) {
          dashLineHeight = topGraphHeight + outLineWidth;
        } else {
          dashLineHeight = topGraphHeight;
        }
    
        var dashWidth = 2;
        var dashSpace = 1;
        var startY = popTextHeight;
        final space = (dashSpace + dashWidth);
    
        while (startY < dashLineHeight) {
          canvas.drawLine(
              Offset(
                  pagePadding +
                      columnWidth * key -
                      columnWidth / 2 +
                      columnPadding * (key - 1),
                  startY),
              Offset(
                  pagePadding +
                      columnWidth * key -
                      columnWidth / 2 +
                      columnPadding * (key - 1),
                  startY + dashWidth),
              _linePaint);
          startY += space;
        }
      }
      
    
    • 画popup文言背景图

    popup背景是图片的,可以用drawImageRect方法实现,为了节约代码同样用extension写一个拓展。

    extension Draw on ui.Canvas {
    
     void drawImageLTWH(
       ui.Image image,
       double left,
       double top,
       Paint paint, {
       double width,
       double height,
     }) {
       drawImageRect(
           image,
           Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()),
           Rect.fromLTWH(left, top, width ?? image.width.toDouble(),
               height ?? image.height.toDouble()),
           paint);
     }
    }
    

    在画图之前我们要把图片加载到内存里,这里使用 rootBundle.load方法加载图片。在flutter中加载图片是比较慢的,通常使用异步的方法加载图片。为了提高性能也可以考虑做图片预加载。

    Future<ui.Image> load(String asset) async {
       var data = await rootBundle.load(asset);
       var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
       var fi = await codec.getNextFrame();
       return fi.image;
     }
     
    

    view的点击事件

    view的点击事件不像android一样需要考虑点击事件的分发,只需要关注点击的位置就可以了。
    我们用 GestureDetector套在widget的外部就可以获取到整个widget所有的点击事件。通过实现onTabDown方法可以获取到点击的状态。
    TapDownDetails.localPosition 可以精准的获取到相对于这个widget的坐标。

    view计算

    最后也是最繁复的一步就是计算不同部分的view的位置。
    按照上面做的拆分我们可以按照从上到下从左到右的方式把view画出来。
    与android不同的是,我们不用像android那样过多的考虑父view测量问题。
    整个画布的大小变动都会通过paint方法回调的Size参数获取到。
    另外点击事件响应后,可以通过改变widget state更新view。

    void paint(Canvas canvas, Size size)

    整体代码

    CustomPainter代码

    import 'dart:ui' as ui;
    import 'package:flitm/resource/colors.dart';
    import 'package:flitm/view/screen/walk/widget/ImageLoader.dart';
    import 'package:flutter/material.dart';
    import 'package:flutter/widgets.dart';
    
    enum ChartLayoutType {
     day,
     week,
     month,
     year,
    }
    
    class ColumnData {
     final double mount;
     final String date;
    
     ColumnData({this.mount = 0, this.date = ''});
    }
    
    typedef OnTouchDown = void Function(TapDownDetails details);
    
    class CustomChartPaint extends CustomPainter {
     Paint _columnPaint;
     Paint _linePaint;
     Paint _backgroundPaint;
     Paint _imagePaint;
    
     static const List<String> graduationText = [
       '0',
       '500',
       '1000',
       '1500',
       '2000',
       '2500'
     ];
    
     static const bottomTextTopMargin = 16.0;
    
     static const bottomTextHeight = 20.0;
    
     static const bottomTextAreaHeight = 36.0;
    
     static const outLineWidth = 10;
    
     static const outOfRangeTopRegionHeight = 22.0;
    
     static const popTextHeight = 44.0;
    
     static const Map<int, String> weekBottomText = {
       1: '日',
       2: '月',
       3: '火',
       4: '水',
       5: '木',
       6: '金',
       7: '土'
     };
    
     static const Map<int, String> dayBottomText = {
       1: '0:00',
       4: '',
       7: '6:00',
       10: '',
       13: '12:00',
       16: '',
       19: '18:00',
       22: ''
     };
    
     static const Map<int, String> monthBottomText = {
       7: '7日',
       14: '14日',
       21: '21日',
       28: '28日'
     };
    
     static const Map<int, String> yearBottomText = {
       1: '',
       2: '',
       3: '3月',
       4: '',
       5: '',
       6: '6月',
       7: '',
       8: '',
       9: '9月',
       10: '',
       11: '',
       12: '12月'
     };
    
     double columnWidth = 22.0;
    
     double columnPadding = 20.0;
    
     double columnTopPadding = 20.0;
    
     double pagePadding = 20.0;
    
     double rightTextWidth;
    
     double topGraphHeight;
    
     BuildContext context;
    
     ChartLayoutType currentChartLayoutType;
    
     Rect columnRect;
    
     var maxRange = 2500.0;
    
     var maxColumnCount = 0;
    
     ui.RRect columnRoundRect;
    
     TapDownDetails tabDownloadDetails;
    
     ui.Image imageCenter;
     ui.Image imageLeft;
     ui.Image imageRight;
     ui.Image imageTriangle;
    
     List<ColumnData> columnDataList;
    
     Map<int, String> bottomTextList = {};
    
     CustomChartPaint(this.context, this.columnDataList,
         {this.currentChartLayoutType = ChartLayoutType.day,
         this.tabDownloadDetails}) {
       initPaint();
       initData();
     }
    
     void initData() {
       imageCenter = ImageLoader.getInstance().getImage(ImageLoader.markUpCenter);
       imageLeft = ImageLoader.getInstance().getImage(ImageLoader.markUpLeft);
       imageRight = ImageLoader.getInstance().getImage(ImageLoader.markUpRight);
       imageTriangle =
           ImageLoader.getInstance().getImage(ImageLoader.markUpTriangle);
       switch (currentChartLayoutType) {
         case ChartLayoutType.day:
           columnWidth = 7.0;
           pagePadding = 16;
           bottomTextList = dayBottomText;
           maxColumnCount = 24;
           break;
         case ChartLayoutType.week:
           columnWidth = 15.0;
           pagePadding = 28;
           bottomTextList = weekBottomText;
           maxColumnCount = 7;
    
           break;
         case ChartLayoutType.month:
           columnWidth = 5.0;
           pagePadding = 7;
           bottomTextList = monthBottomText;
           maxColumnCount = 31;
    
           break;
         case ChartLayoutType.year:
           columnWidth = 13.0;
           pagePadding = 20;
           bottomTextList = yearBottomText;
           maxColumnCount = 12;
           break;
       }
     }
    
     void initPaint() {
       _columnPaint = Paint()..isAntiAlias = true;
       _linePaint = Paint()..strokeWidth = 1;
       _backgroundPaint = Paint()
         ..style = PaintingStyle.fill
         ..isAntiAlias = true;
       _imagePaint = Paint()..isAntiAlias = true;
     }
    
     @override
     void paint(Canvas canvas, Size size) {
       //calculate data we need
       _viewPreCalculate(size);
       //draw black-white area
       _drawBackgroundAndRightText(canvas, size);
    
       bottomTextList.forEach((key, value) {
         _drawBottomText(canvas, size, key, value);
         _drawVerticalDashLine(canvas, key, value);
       });
    
       if (columnDataList.isNotEmpty) {
         for (var i = 1; i <= columnDataList.length; i++) {
           _calculateColumnRect(i, size);
           var from = Offset(columnRect.left, columnRect.top);
           var to = Offset(columnRect.right, columnRect.bottom);
           _drawAllColumn(canvas, from, to);
           // tab down action
           if (tabDownloadDetails != null &&
               tabDownloadDetails.localPosition.dx >
                   columnRect.left - columnPadding / 2 &&
               tabDownloadDetails.localPosition.dx <
                   columnRect.right + columnPadding / 2) {
             _drawSelectColumn(canvas, from, to, i);
             _drawPopMessage(canvas, size, i);
           }
         }
       }
    
       // draw bottom line
       _drawBottomLine(canvas, size);
     }
    
     void _viewPreCalculate(Size size) {
       rightTextWidth = calculateTextSize(true);
       topGraphHeight = size.height - bottomTextAreaHeight;
       columnPadding = ((size.width - rightTextWidth - 4) -
               2 * pagePadding -
               (maxColumnCount - 1) * columnWidth) /
           (maxColumnCount - 1);
     }
    
     void _calculateColumnRect(int i, Size size) {
       var rectLeft =
           pagePadding + columnPadding * (i - 1) + columnWidth * (i - 1);
       var rectTop = outOfRangeTopRegionHeight +
           popTextHeight +
           (size.height - columnTopPadding) *
               (maxRange - columnDataList[i - 1].mount) /
               maxRange;
       var rectRight = pagePadding + columnWidth * i + columnPadding * (i - 1);
       columnRect = Rect.fromLTRB(rectLeft, rectTop, rectRight, topGraphHeight);
     }
    
     void _drawAllColumn(ui.Canvas canvas, Offset from, Offset to) {
       _columnPaint.shader = ui.Gradient.linear(from, to, [
         BeautyColors.colorColumnBackground1st,
         BeautyColors.colorColumnBackgroundSec,
         BeautyColors.colorColumnBackgroundTrd
       ], [
         0,
         0.5,
         1
       ]);
       _columnPaint.style = PaintingStyle.fill;
       columnRoundRect = RRect.fromRectAndCorners(columnRect,
           topLeft: ui.Radius.circular(10), topRight: Radius.circular(10));
       canvas.drawRRect(columnRoundRect, _columnPaint);
       _columnPaint.shader = ui.Gradient.linear(from, to, [
         BeautyColors.colorColumn1st,
         BeautyColors.colorColumnSec,
         BeautyColors.colorColumnTrd
       ], [
         0,
         0.5,
         1
       ]);
       canvas.drawRRect(columnRoundRect, _columnPaint);
       _columnPaint.shader = ui.Gradient.linear(
           from, to, [BeautyColors.blue01, BeautyColors.blue01]);
       _columnPaint.style = PaintingStyle.stroke;
       _columnPaint.strokeWidth = 1.0;
       canvas.drawRRect(columnRoundRect, _columnPaint);
     }
    
     void _drawSelectColumn(ui.Canvas canvas, Offset from, Offset to, int i) {
       _columnPaint.shader = ui.Gradient.linear(
           from, to, [BeautyColors.pink01, BeautyColors.pink01]);
       _columnPaint.style = PaintingStyle.fill;
       canvas.drawRRect(columnRoundRect, _columnPaint);
       _columnPaint.style = PaintingStyle.stroke;
       canvas.drawRRect(columnRoundRect, _columnPaint);
       _linePaint.color = BeautyColors.pink01;
       double lineEnd;
       if (bottomTextList[i] != null && bottomTextList[i].isNotEmpty) {
         lineEnd = columnRect.bottom + outLineWidth;
       } else {
         lineEnd = columnRect.bottom;
       }
       canvas.drawLine(
           Offset(
               pagePadding +
                   columnWidth * i -
                   columnWidth / 2 +
                   columnPadding * (i - 1),
               popTextHeight),
           Offset(
               pagePadding +
                   columnWidth * i -
                   columnWidth / 2 +
                   columnPadding * (i - 1),
               lineEnd),
           _linePaint);
     }
    
     void _drawPopMessage(ui.Canvas canvas, Size size, int i) {
       double messageWidth;
       double messageLeft;
       //todo  not a specific text append method
       var startText = '';
       var stepText = '${columnDataList[i - 1].mount}歩';
       var month = '10月';
       switch (currentChartLayoutType) {
         case ChartLayoutType.day:
           startText = '$i:00 ~ ${i + 1}:00';
           break;
         case ChartLayoutType.week:
           startText = columnDataList[i - 1].date;
           break;
         case ChartLayoutType.month:
           startText = month + '$i日';
           break;
         case ChartLayoutType.year:
           startText = '$i月';
           break;
       }
    
       var startTextWidth =
           calculateTextSize(true, fontSize: 14, value: startText);
       var startTextHeight =
           calculateTextSize(false, fontSize: 14, value: startText);
       var stepTextWidth = calculateTextSize(true,
           fontSize: 14, fontWeight: FontWeight.bold, value: stepText);
       var stepTextHeight = calculateTextSize(false,
           fontSize: 14, fontWeight: FontWeight.bold, value: stepText);
       messageWidth = startTextWidth + stepTextWidth + 2 * 6 + 8;
    
       if (pagePadding +
               columnWidth * i -
               columnWidth / 2 +
               columnPadding * (i - 1) <=
           (messageWidth + 12) / 2) {
         messageLeft = 0;
       } else if (pagePadding +
               columnWidth * i -
               columnWidth / 2 +
               columnPadding * (i - 1) >=
           size.width - (messageWidth + 12) / 2) {
         messageLeft = size.width - (messageWidth + 12);
       } else {
         messageLeft = pagePadding +
             columnWidth * i -
             columnWidth / 2 +
             columnPadding * (i - 1) -
             messageWidth / 2 -
             6;
       }
    
       if (imageCenter != null) {
         canvas.drawImageRect(
             imageCenter,
             Rect.fromLTWH(0, 0, imageCenter.width.toDouble(),
                 imageCenter.height.toDouble()),
             Rect.fromLTWH(messageLeft + 6, 0, messageWidth, 34),
             _imagePaint);
       }
    
       if (imageLeft != null) {
         canvas.drawImageLTWH(imageLeft, messageLeft, 0, _imagePaint,
             width: 6, height: 34);
       }
    
       if (imageRight != null) {
         canvas.drawImageLTWH(
             imageRight, messageLeft + messageWidth + 6, 0, _imagePaint,
             width: 6, height: 34);
       }
    
       if (imageTriangle != null) {
         canvas.drawImageLTWH(
             imageTriangle,
             pagePadding +
                 columnWidth * i -
                 columnWidth / 2 +
                 columnPadding * (i - 1) -
                 5,
             32,
             _imagePaint,
             width: 10,
             height: 8);
       }
    
       var textXOffSet = messageLeft + 6 + 6;
       var textYOffset = (34 - startTextHeight) / 2;
       var leftTextOffset = Offset(textXOffSet, textYOffset);
    
       canvas.drawText(
         leftTextOffset,
         ui.TextStyle(color: BeautyColors.gray02),
         startText,
         fontSize: 14,
         maxWidth: startTextWidth,
       );
    
       var stepTextXOffSet = messageLeft + 6 + 6 + startTextWidth + 8;
       var stepTextYOffset = (34 - stepTextHeight) / 2;
       var stepTextOffset = Offset(stepTextXOffSet, stepTextYOffset);
    
       canvas.drawText(
         stepTextOffset,
         ui.TextStyle(color: BeautyColors.gray02),
         stepText,
         fontSize: 14,
         fontWeight: FontWeight.bold,
         maxWidth: stepTextWidth,
       );
     }
    
     double calculateTextSize(bool isWidth,
         {double maxWidth = 200,
         double fontSize = 13,
         String value = '12345',
         FontWeight fontWeight = FontWeight.normal,
         int maxLines = 1}) {
       var painter = TextPainter(
           locale: Localizations.localeOf(context, nullOk: true),
           maxLines: maxLines,
           textDirection: TextDirection.ltr,
           text: TextSpan(
               text: value,
               style: TextStyle(
                 fontWeight: fontWeight,
                 fontSize: fontSize,
               )));
       painter.layout(maxWidth: maxWidth);
       if (isWidth) {
         return painter.width;
       } else {
         return painter.height;
       }
     }
    
     void _drawBackgroundAndRightText(ui.Canvas canvas, Size size) {
       // draw area
       //todo not accomplished much
       var backgroundHeight =
           (topGraphHeight - outOfRangeTopRegionHeight - popTextHeight) / 5;
       _backgroundPaint.color = BeautyColors.gray04_50;
       for (var n = 1; n <= 5; n++) {
         n % 2 == 1
             ? _backgroundPaint.color = BeautyColors.gray04_50
             : _backgroundPaint.color = BeautyColors.white01;
         canvas.drawRect(
             Rect.fromLTRB(
                 0,
                 topGraphHeight - backgroundHeight * n,
                 size.width - rightTextWidth - 4,
                 topGraphHeight - backgroundHeight * (n - 1)),
             _backgroundPaint);
       }
    
       var wordsHeight = calculateTextSize(false);
       for (var n = 1; n <= 6; n++) {
         var textXOffSet = size.width - rightTextWidth;
         var textYOffset = size.height -
             bottomTextHeight -
             bottomTextTopMargin -
             backgroundHeight * (n - 1) -
             wordsHeight / 2;
         var rightTextOffset = Offset(textXOffSet, textYOffset);
         canvas.drawText(rightTextOffset, ui.TextStyle(color: BeautyColors.gray02),
             graduationText[n - 1],
             maxWidth: rightTextWidth, fontWeight: FontWeight.normal);
       }
     }
    
     void _drawBottomText(ui.Canvas canvas, Size size, int key, String value) {
       var bottomTextWidth =
           calculateTextSize(true, value: value, fontWeight: FontWeight.bold);
       var textHalfWidth = (bottomTextWidth / 2);
       var textXOffSet = pagePadding +
           columnPadding * (key - 1) +
           columnWidth * (key - 1) +
           columnWidth / 2 -
           textHalfWidth;
       var bottomTextOffset = Offset(textXOffSet, size.height - bottomTextHeight);
       canvas.drawText(
           bottomTextOffset, ui.TextStyle(color: BeautyColors.blue01), value,
           fontWeight: FontWeight.bold,
           textAlign: TextAlign.center,
           maxWidth: bottomTextWidth);
     }
    
     void _drawVerticalDashLine(ui.Canvas canvas, int key, String value) {
       _linePaint.color = BeautyColors.colorChartDash;
       double dashLineHeight;
       if (value != null && value.isNotEmpty) {
         dashLineHeight = topGraphHeight + outLineWidth;
       } else {
         dashLineHeight = topGraphHeight;
       }
    
       var dashWidth = 2;
       var dashSpace = 1;
       var startY = popTextHeight;
       final space = (dashSpace + dashWidth);
    
       while (startY < dashLineHeight) {
         canvas.drawLine(
             Offset(
                 pagePadding +
                     columnWidth * key -
                     columnWidth / 2 +
                     columnPadding * (key - 1),
                 startY),
             Offset(
                 pagePadding +
                     columnWidth * key -
                     columnWidth / 2 +
                     columnPadding * (key - 1),
                 startY + dashWidth),
             _linePaint);
         startY += space;
       }
     }
    
     @override
     bool shouldRepaint(CustomPainter oldDelegate) {
       return true;
     }
    
     void _drawBottomLine(ui.Canvas canvas, ui.Size size) {
       var start = ui.Offset(0, topGraphHeight);
       var end = ui.Offset(
           size.width - rightTextWidth - 4, size.height - bottomTextAreaHeight);
       _linePaint.color = BeautyColors.blue01;
       canvas.drawLine(start, end, _linePaint);
     }
    }
    
    extension Draw on ui.Canvas {
     void drawText(
       Offset offset,
       ui.TextStyle textStyle,
       String value, {
       TextAlign textAlign = TextAlign.start,
       double fontSize = 13,
       double maxWidth = 100.0,
       FontWeight fontWeight = FontWeight.normal,
     }) {
       var paragraphBuilder = ui.ParagraphBuilder(
         ui.ParagraphStyle(
             textAlign: textAlign, fontSize: fontSize, fontWeight: fontWeight),
       )..pushStyle(textStyle);
       paragraphBuilder.addText(value);
       var pc = ui.ParagraphConstraints(width: maxWidth);
       var textParagraph = paragraphBuilder.build()..layout(pc);
       drawParagraph(textParagraph, offset);
     }
    
     void drawImageLTWH(
       ui.Image image,
       double left,
       double top,
       Paint paint, {
       double width,
       double height,
     }) {
       drawImageRect(
           image,
           Rect.fromLTWH(0, 0, image.width.toDouble(), image.height.toDouble()),
           Rect.fromLTWH(left, top, width ?? image.width.toDouble(),
               height ?? image.height.toDouble()),
           paint);
     }
    }
    
    
    

    ImageLoader代码

    class ImageLoader {
      ImageLoader._();
    
      static final _instance = ImageLoader._();
    
      factory ImageLoader.getInstance() => _instance;
    
      Map<String, ui.Image> imagePool = {};
    
      static const markUpCenter = 'mark_up_center';
      static const markUpTriangle = 'mark_up_Triangle';
      static const markUpLeft = 'mark_up_left';
      static const markUpRight = 'mark_up_right';
    
      void preCacheImage() {
        load('assets/3x/ic_markup_1_center.png').then((value) {
          imagePool.addAll({markUpCenter: value});
        });
        load('assets/3x/ic_markup_1.png').then((value) {
          imagePool.addAll({markUpTriangle: value});
        });
        load('assets/3x/ic_markup_1_left.png').then((value) {
          imagePool.addAll({markUpLeft: value});
        });
        load('assets/3x/ic_markup_1_right.png').then((value) {
          imagePool.addAll({markUpRight: value});
        });
      }
    
      void clearImageCache() {
        imagePool = null;
      }
    
      ui.Image getImage(String name) {
        return imagePool[name];
      }
    
      Future<ui.Image> load(String asset) async {
        var data = await rootBundle.load(asset);
        var codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
        var fi = await codec.getNextFrame();
        return fi.image;
      }
    }
    
    

    在widget中使用代码

    Widget _getContent() {
        return Column(
          children: [
            Container(
              decoration: BoxDecoration(
                color:Colors.blue,
              ),
              height: 190,
            ),
            // padding: EdgeInsets.only(left: 16,right: 16),
            Expanded(
                flex: 1,
                child: Container(
                  padding: EdgeInsets.only(left: 16, right: 16, bottom: 30),
                  child: SizedBox(
                      width: double.infinity,
                      child: GestureDetector(
                        onTapDown: (detail) {
                          print('ontouchdown${detail.localPosition.dx}');
                          setState(() {
                            details = detail;
                          });
                        },
                        child: CustomPaint(
                          painter: CustomChartPaint(context, doubleList,
                              currentChartLayoutType: type,
                              tabDownloadDetails: details),
                        ),
                      )),
                )),
          ],
        );
      }
    

    相关文章

      网友评论

          本文标题:用CustomPaint自定义ChartView

          本文链接:https://www.haomeiwen.com/subject/xizbvktx.html