Skip to content

using LayoutBuilder's constraints but not screen size to draw canvas#4

Open
corkine wants to merge 1 commit intosmartbackme:mainfrom
corkine:main
Open

using LayoutBuilder's constraints but not screen size to draw canvas#4
corkine wants to merge 1 commit intosmartbackme:mainfrom
corkine:main

Conversation

@corkine
Copy link

@corkine corkine commented Nov 1, 2023

On large screen devices, drawing the Canvas using its full width may result in layout errors, and using constraints from a LayoutBuilder may be a better idea.

图片

return Scaffold(
      body: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            color: Colors.grey,
            width: 400,
            height: 400,
            child: RadarWidget(

@adnanjpg
Copy link

adnanjpg commented Nov 5, 2023

hey,

thanks for the work you've put in

I've been having this problem for a while, but the new solution here does not seem to solve it entirely

example case (the main.dart under the example folder but slightly modified):

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Container(
          alignment: AlignmentDirectional.center,
          child: Row(
            children: [
              const Expanded(
                // flex: 1,
                child: SizedBox(),
              ),
              Expanded(
                child: Builder(builder: (context) {
                  return RadarWidget(
                    skewing: 0,
                    isNeedDrawLegend: true,
                    isNeedDrawVisualMap: true,
                    radarMap: RadarMapModel(
                      alpha: 200,
                      // legend: [
                      //   LegendModel('Low', HexColor('#EDEEEE')),
                      //   // LegendModel('Low', HexColor('#CEE8EA')),
                      //   LegendModel('Decreased', HexColor('#CEE8EA')),
                      //   // LegendModel('Normal', HexColor('#6EC9D1')),
                      //   LegendModel('Normal', HexColor('#6EC9D1')),
                      //   // LegendModel('High', Colors.green),
                      // ],
                      splitAreaStyle: const SplitAreaStyle(
                        colors: [Colors.blue, Colors.blue],
                        alpha: 255,
                      ),
                      visualMap: VisualMap(
                          colors: [Colors.blue, Colors.blue],
                          texts: ['Low', 'Normal']),
                      indicator: [
                        IndicatorModel("English", 4),
                        IndicatorModel("Physics", 4),
                        IndicatorModel("Chemistry", 4),
                        IndicatorModel("Biology", 4),
                      ],
                      data: [
                        MapDataModel(
                          [2, 3, 1, 2],
                          dataMarkerStyle: const DataMarkerStyle(
                            size: 3,
                            color: Colors.blue,
                          ),
                          connectLineStyle:
                              ConnectLineStyle(width: 6, color: Colors.red),
                        ),
                      ],
                      radius: 100,
                      shape: Shape.square,
                      maxWidth: 80,
                      line: LineModel(4),
                    ),
                    textStyle:
                        const TextStyle(color: Colors.black, fontSize: 14),
                    // lineText: (p, length) => "${(p * 100 ~/ length)}%",
                    // dilogText: (IndicatorModel indicatorModel,
                    //     List<LegendModel> legendModels, List<double> mapDataModels) {
                    //   StringBuffer text = StringBuffer("");
                    //   for (int i = 0; i < mapDataModels.length; i++) {
                    //     text.write(
                    //         "${legendModels[i].name} : ${mapDataModels[i].toString()}");
                    //     if (i != mapDataModels.length - 1) {
                    //       text.write("\n");
                    //     }
                    //   }
                    //   return text.toString();
                    // },
                    // outLineText: (data, max) => "${data * 100 ~/ max}%",
                  );
                }),
              ),
            ],
          ),
        ),
      ),
    );
  }

here's the result:

Screenshot_1699180555

@corkine
Copy link
Author

corkine commented Nov 6, 2023

I can't see what the problem is. Both Expanded in Row contain 1 flex, and they share the entire width. However, since drawing RadarWidget requires a certain size, when the total width is insufficient, the child component will ignore the recommended width of the parent component and use the actual size of its own grandchild component. Report and layout.

Do you mean the RadarWidget here should scale itself to satisfy the layout constraint of always being 1/2 width? When adjusting the available width, you can always see that the LayoutBuilder's BoxConstraint.maxWidth is always half of the available width. How RadarWidget draws the Canvas to use this data depends on the internal implementation, but I think it is better to scale it to invisible , this method is quite good now.

图片

@adnanjpg
Copy link

adnanjpg commented Nov 6, 2023

can you provide an example of passing the BoxConstraint.maxWidth toRadarWidget?

child: Padding(
padding: EdgeInsets.only(right: sk),
child: Container(
LayoutBuilder(builder: (context, constraints) {
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#4 @adnanjpg add debug point or print here to see constraints

@adnanjpg
Copy link

adnanjpg commented Nov 7, 2023

can you please provide the code that outputs the image above?

@corkine
Copy link
Author

corkine commented Nov 7, 2023

import 'package:flutter/material.dart';

import 'vendor/flutter_radar_map.dart';
import 'vendor/radar_map_model.dart';

class TestApp extends StatefulWidget {
  const TestApp({super.key});

  @override
  State<TestApp> createState() => _TestAppState();
}

class _TestAppState extends State<TestApp> {
  @override
  Widget build(BuildContext context) {
      return MaterialApp(
        home: Scaffold(
          appBar: AppBar(
            title: const Text('Plugin example app'),
          ),
          body: Container(
            alignment: AlignmentDirectional.center,
            child: Row(
              children: [
                const Expanded(
                  // flex: 1,
                  child: SizedBox(),
                ),
                Expanded(
                  child: Builder(builder: (context) {
                    return RadarWidget(
                      skewing: 0,
                      isNeedDrawLegend: true,
                      //isNeedDrawVisualMap: true,
                      radarMap: RadarMapModel(
                        alpha: 200,
                        legend: [
                          LegendModel('Low', Colors.red),
                          // // LegendModel('Low', HexColor('#CEE8EA')),
                          // LegendModel('Decreased', Colors.pinkAccent),
                          // // LegendModel('Normal', HexColor('#6EC9D1')),
                          // LegendModel('Normal', Colors.blue),
                          // LegendModel('High', Colors.green),
                        ],
                        // splitAreaStyle: const SplitAreaStyle(
                        //   colors: [Colors.blue, Colors.blue],
                        //   alpha: 255,
                        // ),
                        // visualMap: VisualMap(
                        //     colors: [Colors.blue, Colors.blue],
                        //     texts: ['Low', 'Normal']),
                        indicator: [
                          IndicatorModel("English", 4),
                          IndicatorModel("Physics", 4),
                          IndicatorModel("Chemistry", 4),
                          IndicatorModel("Biology", 4),
                        ],
                        data: [
                          MapDataModel(
                            [2, 3, 1, 2],
                            // dataMarkerStyle: const DataMarkerStyle(
                            //   size: 3,
                            //   color: Colors.blue,
                            // ),
                            // connectLineStyle:
                            // ConnectLineStyle(width: 6, color: Colors.red),
                          ),
                        ],
                        radius: 100,
                        shape: Shape.square,
                        maxWidth: 80,
                        line: LineModel(4),
                      ),
                      textStyle:
                      const TextStyle(color: Colors.black, fontSize: 14),
                      // lineText: (p, length) => "${(p * 100 ~/ length)}%",
                      // dilogText: (IndicatorModel indicatorModel,
                      //     List<LegendModel> legendModels, List<double> mapDataModels) {
                      //   StringBuffer text = StringBuffer("");
                      //   for (int i = 0; i < mapDataModels.length; i++) {
                      //     text.write(
                      //         "${legendModels[i].name} : ${mapDataModels[i].toString()}");
                      //     if (i != mapDataModels.length - 1) {
                      //       text.write("\n");
                      //     }
                      //   }
                      //   return text.toString();
                      // },
                      // outLineText: (data, max) => "${data * 100 ~/ max}%",
                    );
                  }),
                ),
              ],
            ),
          ),
        ),
      );
    }
}

with this

import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';

import 'radar_map_model.dart';
import 'radar_utils.dart';

@immutable
class RadarWidget extends StatefulWidget {
  //数据传入
  final RadarMapModel radarMap;

  //文字风格
  final TextStyle? textStyle;

  //是否绘制图例
  final bool? isNeedDrawLegend;

  final LineText? lineText;
  final DialogText? dialogText;
  final OutLineText? outLineText;

  final double? skewing;

  RadarWidget(
      {Key? key,
      required this.radarMap,
      this.textStyle = const TextStyle(color: Colors.black),
      this.isNeedDrawLegend = true,
      this.lineText,
      this.dialogText,
      this.outLineText,
      this.skewing})
      : super(key: key) {
    assert(radarMap.legend.length == radarMap.data.length);
  }

  @override
  State<StatefulWidget> createState() => _RadarMapWidgetState();
}

class _RadarMapWidgetState extends State<RadarWidget>
    with SingleTickerProviderStateMixin {
  // double _angle = 0.0;
  // late AnimationController controller; // 动画控制器
  // late Animation<double> animation; // 动画实例
  double top = 0;
  double bottom = 0;
  List<Rect> node = [];
  TapModel tab = TapModel();
  final _counter = ValueNotifier(0);

  @override
  void initState() {
    super.initState();

    // // 创建 Animation对象
    // controller = AnimationController(duration: Duration(milliseconds: widget.radarMap.duration), vsync: this);
    // // 创建曲线插值器
    // var curveTween = CurveTween(curve: Cubic(0.96, 0.13, 0.1, 1.2));
    // // 定义估值器
    // var tween = Tween(begin: 0.0, end: 360.0);
    // // 插值器根据时间产生值,并提供给估值器,作为animation的value
    // animation = tween.animate(curveTween.animate(controller));
    // animation.addListener(() {
    //   setState(() {
    //     _angle = animation.value;
    //   });
    // });
    // controller.forward();
  }

  @override
  void dispose() {
    // controller.dispose();
    // animation.removeListener(() {});
    super.dispose();
  }

  ///构建图例
  Widget buildLegend(String legendTitle, Color legendColor,
      {Color? textColor, double? textFontSize}) {
    return Row(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisSize: MainAxisSize.min,
        children: [
          Container(
              width: 12,
              height: 12,
              margin: const EdgeInsets.only(right: 4),
              decoration: BoxDecoration(
                  color: legendColor,
                  borderRadius: const BorderRadius.all(Radius.circular(6)))),
          Text(legendTitle,
              style: TextStyle(
                  fontSize: textFontSize ?? 10,
                  color: textColor ?? Colors.black))
        ]);
  }

  late int elementLength; //维度 数量

  // RadarUtils.getHeight(widget.radarMap.radius, widget.radarMap.indicator.length
  @override
  Widget build(BuildContext context) {
    double sk = (widget.skewing ?? 0.0) > 40 ? 40 : (widget.skewing ?? 0.0);
    if (sk < 0) {
      sk = 0;
    }

    // var center = Transform.rotate(
    //   // 旋转动画
    //   angle: -_angle / 180 * pi,
    //   child: Transform.scale(
    //     // 缩放动画
    //     scale: 0.5 + animation.value / 360 / 2,
    //     child: GestureDetector(child: paint,
    //       onTapUp: (TapUpDetails details){
    //         painter.tapUp(details);
    //       },
    //       onTapDown: (TapDownDetails details){
    //         painter.tapDown(details);
    //       },
    //       // onTap: (){
    //       //  print("onTap");
    //       // },
    //     ),
    //   ),
    // );

    return Column(children: [
      LayoutBuilder(
        builder: (context, cont) {
          var painter = RadarMapPainter(cont.maxWidth, top, widget.radarMap,
              (t, b) {
            setState(() {
              top = t;
              bottom = b;
            });
          }, node, tab, sk,
              textStyle: widget.textStyle,
              lineText: widget.lineText,
              outLineText: widget.outLineText,
              dialogText: widget.dialogText,
              repaint: _counter);
          return GestureDetector(
              onTapDown: (e) => painter.tapDown(e),
              onTapUp: (e) => painter.tapUp(e),
              child: CustomPaint(
                  size: Size(
                      cont.maxWidth,
                      RadarUtils.getHeight(
                              widget.radarMap.radius,
                              widget.radarMap.indicator.length,
                              widget.radarMap.shape) +
                          bottom +
                          top),
                  painter: painter));
        },
      ),
      Offstage(
          offstage: !widget.isNeedDrawLegend!,
          child: Padding(
              padding: EdgeInsets.only(right: sk),
              child: Container(
                  width: double.infinity,
                  margin: const EdgeInsets.only(
                      top: 20, bottom: 20, left: 30, right: 30),
                  child: Wrap(
                    spacing: 8.0,
                    runSpacing: 4.0,
                    alignment: WrapAlignment.spaceAround,
                    children: widget.radarMap.legend
                        .map((item) => buildLegend(item.name, item.color,
                            textColor: item.textColor,
                            textFontSize: item.textFontSize))
                        .toList(),
                  ))))
    ]);
  }
// MainAxisAlignment.spaceAround
}

typedef LineText = String Function(int p, int length);
typedef OutLineText = String Function(double data, double maxValue);
typedef DialogText = String Function(IndicatorModel indicatorModel,
    List<LegendModel> legendModels, List<double> mapDataModels);
typedef WidthHeight = Function(double w, double h);

/// canvas绘制
class RadarMapPainter extends CustomPainter {
  RadarMapModel radarMap;
  late Paint mLinePaint; // 线画笔
  late Paint mLineInnerPaint; // 线画笔
  late Paint mAreaPaint; // 区域画笔
  Paint? mFillPaint; // 填充画笔
  TextStyle? textStyle;
  late Path mLinePath; // 短直线路径
  late Path mDialogPath; // 短直线路径
  late Paint mDialogPaint; // 短直线路径
  late int elementLength; //维度 数量
  LineText? lineText;
  DialogText? dialogText;
  OutLineText? outLineText;
  final WidthHeight _widthHeight;
  double w;
  double top;
  List<Rect> node;
  TapModel tab;
  double skewing;

  RadarMapPainter(this.w, this.top, this.radarMap, this._widthHeight, this.node,
      this.tab, this.skewing,
      {this.textStyle,
      this.lineText,
      this.dialogText,
      this.outLineText,
      Listenable? repaint})
      : super(repaint: repaint) {
    mLinePath = Path();
    mDialogPath = Path();
    mLinePaint = Paint()
      ..color = Colors.grey
      ..style = PaintingStyle.stroke
      ..strokeWidth = 0.008 * radarMap.radius
      ..isAntiAlias = true;
    mDialogPaint = Paint() //填充画笔
      ..color = const Color(0xE64C4C4C)
      ..isAntiAlias = true;
    mFillPaint = Paint() //填充画笔
      ..strokeWidth = 0.05 * radarMap.radius
      ..color = Colors.black
      ..isAntiAlias = true;
    mLineInnerPaint = Paint()
      ..strokeWidth = 1.5
      ..style = PaintingStyle.stroke
      ..strokeJoin = StrokeJoin.round;
    mAreaPaint = Paint()..isAntiAlias = true;
    elementLength = radarMap.indicator.length;
  }

  void tapUp(TapUpDetails details) {}

  void tapDown(TapDownDetails details) {
    // // print("${details.globalPosition.dx-w/2} . ${details.globalPosition.dy-radarMap.radius-top}");
    // print("${details.localPosition.dx-w/2} . ${details.localPosition.dy-radarMap.radius-top}");
    // print("${node[0].left},${node[0].right},${node[0].top},${node[0].bottom}");
    // // print("${details.globalPosition.dx} . ${details.globalPosition.dy}");
    // // print("${details.localPosition.dx} . ${details.localPosition.dy}");
    // // print("${node.length}");
    for (int i = 0; i < node.length; i++) {
      // print("${node[i].left} . ${node[i].top}");
      var n = node[i];
      var x = details.localPosition.dx - w / 2;
      var y = details.localPosition.dy - radarMap.radius - top;

      if (x >= n.left && x <= n.right && y >= n.top && y <= n.bottom) {
        tab
          ..x = (x + skewing)
          ..y = y
          ..index = i;
        return;
      }
    }
    tab.reset();
  }

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(w / 2 - skewing, radarMap.radius + top); // 移动坐标系
    drawInnerCircle(canvas, size);
    for (int i = 0; i < radarMap.legend.length; i++) {
      drawRadarMap(
          canvas,
          radarMap.data[i].data,
          radarMap.indicator.map((item) => item.maxValues).toList(),
          mAreaPaint
            ..color = radarMap.legend[i].color.withAlpha(radarMap.alpha));
      drawRadarPath(
          canvas,
          radarMap.data[i].data,
          radarMap.indicator.map((item) => item.maxValues).toList(),
          mLineInnerPaint..color = radarMap.legend[i].color);
      drawRadarText(
          canvas,
          radarMap.data[i].data,
          radarMap.indicator.map((item) => item.maxValues).toList(),
          radarMap.legend[i].color);
    }

    drawInfoText(canvas);
    drawInfoDialog(canvas);
  }

  @override
  bool shouldRepaint(CustomPainter oldDelegate) {
    return false;
  }

  /// 绘制内圈圆 || 内多边形、分割线  || 绘制文字
  drawInnerCircle(Canvas canvas, Size size) {
    double innerRadius = radarMap.radius; // 内圆半径
    var line = radarMap.line;
    int ring = line?.line ?? 1;
    Color ringColor = line?.color ?? Colors.grey;
    if (radarMap.shape == Shape.circle) {
      /// 绘制五个圆环
      for (int s = ring; s > 0; s--) {
        canvas.drawCircle(
          const Offset(0, 0),
          innerRadius / ring * s,
          mLinePaint
            ..color = ringColor
            ..style = PaintingStyle.stroke,
        );
      }
    } else {
      /// 绘制五个方环

      ///均分圆的度数
      double delta = 2 * pi / elementLength;
      for (int s = ring; s > 0; s--) {
        ///起始位置
        var startRa = innerRadius / ring * s;

        Path mapPath = Path();

        ///角度
        double angle = 0;
        mapPath.moveTo(0, -startRa);
        for (int i = 0; i < elementLength; i++) {
          angle += delta;
          mapPath.lineTo(0 + startRa * sin(angle), 0 - startRa * cos(angle));
        }
        mapPath.close();
        canvas.drawPath(
          mapPath,
          mLinePaint
            ..color = ringColor
            ..style = PaintingStyle.stroke,
        );
      }
    }
    // 图上画文字
    if (lineText != null) {
      // (s/ring).toStringAsFixed(2)
      var maxWidth = 100.0;
      for (int s = ring; s > 0; s--) {
        ///起始位置
        var startRa = innerRadius / ring * s - radarMap.radius * 0.05;
        Offset offset = Offset(-maxWidth / 2, -startRa);
        // fontSize: textStyle!.fontSize ?? radarMap.radius * 0.16,

        final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle(
            textAlign: TextAlign.center,
            fontSize: line?.textFontSize ?? radarMap.radius * 0.1,
            fontWeight: FontWeight.normal));
        paragraphBuilder.pushStyle(ui.TextStyle(
            color: line?.textColor ?? Colors.black,
            textBaseline: ui.TextBaseline.alphabetic));
        paragraphBuilder.addText(lineText!.call(s, ring));
        var paragraph = paragraphBuilder.build();
        paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
        canvas.drawParagraph(paragraph, offset);
      }
    }

    // 遍历画线
    for (var i = 0; i < elementLength; i++) {
      canvas.save();
      canvas.rotate(360 / elementLength * i.toDouble() / 180 * pi);
      mLinePath.moveTo(0, -innerRadius);
      mLinePath.relativeLineTo(0, innerRadius); //线的路径
      canvas.drawPath(
          mLinePath,
          mLinePaint
            ..color = ringColor
            ..style = PaintingStyle.stroke); //绘制线
      canvas.restore();
    }

    canvas.save();
    canvas.restore();
  }

  /// 绘制区域
  drawRadarMap(
      Canvas canvas, List<double> value, List<double> maxList, Paint mapPaint) {
    Path radarMapPath = Path();
    double step = radarMap.radius / elementLength; //每小段的长度
    radarMapPath.moveTo(
        0, -value[0] / (maxList[0] / elementLength) * step); //起点
    for (int i = 1; i < elementLength; i++) {
      double mark = value[i] / (maxList[i] / elementLength);
      var deg = pi / 180 * (360 / elementLength * i - 90);
      radarMapPath.lineTo(mark * step * cos(deg), mark * step * sin(deg));
    }
    radarMapPath.close();
    canvas.drawPath(radarMapPath, mapPaint);
  }

  /// 绘制边框
  drawRadarPath(Canvas canvas, List<double> value, List<double> maxList,
      Paint linePaint) {
    Path mradarPath = Path();
    double step = radarMap.radius / value.length; //每小段的长度
    mradarPath.moveTo(0, -value[0] / (maxList[0] / value.length) * step);
    for (int i = 1; i < value.length; i++) {
      double mark = value[i] / (maxList[i] / value.length);
      var deg = pi / 180 * (360 / value.length * i - 90);
      mradarPath.lineTo(mark * step * cos(deg), mark * step * sin(deg));
    }
    mradarPath.close();
    canvas.drawPath(mradarPath, linePaint);
  }

  void drawRadarText(
      ui.Canvas canvas, List<double> value, List<double> maxList, Color color) {
    if (outLineText != null) {
      // Path mradarPath = Path();
      double step = radarMap.radius / elementLength; //每小段的长度
      // mradarPath.moveTo(0, -value[0] / (maxList[0] / value.length) * step);
      var maxWidth = 40.0;

      for (int i = 0; i < value.length; i++) {
        double mark = value[i] / (maxList[i] / value.length);
        var deg = pi / 180 * (360 / value.length * i - 90);

        final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle(
            textAlign: TextAlign.center,
            fontSize: radarMap.outTextSize ?? 10,
            fontWeight: FontWeight.normal));
        paragraphBuilder.pushStyle(ui.TextStyle(
            color: color, textBaseline: ui.TextBaseline.alphabetic));
        paragraphBuilder.addText(outLineText!.call(value[i], maxList[i]));
        var paragraph = paragraphBuilder.build();
        paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
        var pianyix = cos(deg) * (paragraph.width / 2);
        var pianyiy = sin(deg) * (paragraph.height / 2);
        var of = Offset(mark * step * cos(deg) - paragraph.width / 2 + pianyix,
            mark * step * sin(deg) - paragraph.height / 2 + pianyiy);
        canvas.drawParagraph(paragraph, of);

        // mradarPath.lineTo(mark * step * cos(deg), mark * step * sin(deg));
      }
      // mradarPath.close();
      // canvas.drawPath(mradarPath, linePaint);
    }
  }

  /// 绘制顶点文字
  void drawInfoText(Canvas canvas) {
    // double r2 = radarMap.radius + 2; //下圆半径
    // for (int i = 0; i < elementLength; i++) {
    //   Offset offset;
    //   canvas.save();
    //   if (i != 0) {
    //     canvas.rotate(360 / elementLength * i / 180 * pi + pi);
    //     offset = Offset(-50, r2);
    //   } else {
    //     offset = Offset(-50, -r2 - textStyle!.fontSize! - 8);
    //   }
    //   drawText(
    //     canvas,
    //     radarMap.indicator[i].name,
    //     offset,
    //   );
    //   canvas.restore();
    // }

    double innerRadius = radarMap.radius; // 内圆半径
    double delta = 2 * pi / elementLength;
    var startRa = innerRadius;
    var maxWidth = radarMap.maxWidth ?? 40.0;
    var top = 0.0;
    var bottom = 0.0;

    ///角度
    double angle = 0;
    node.clear();
    for (int i = 0; i < elementLength; i++) {
      // drawText(
      //   canvas,
      //   radarMap.indicator[i].name,
      //   Offset(0 + startRa * sin(angle), 0 - startRa * cos(angle)),
      // );
      final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle(
          textAlign: TextAlign.center,
          fontSize: textStyle!.fontSize ?? radarMap.radius * 0.10,
          fontWeight: FontWeight.normal));
      paragraphBuilder.pushStyle(ui.TextStyle(
          color: textStyle!.color, textBaseline: ui.TextBaseline.alphabetic));
      paragraphBuilder.addText(radarMap.indicator[i].name);
      var paragraph = paragraphBuilder.build();
      paragraph.layout(ui.ParagraphConstraints(width: maxWidth));

      // print("${0 + startRa * sin(angle)-paragraph.width/2}======${0 - startRa * cos(angle) - paragraph.height/2}");
      // print("${0 + startRa * sin(angle)-paragraph.width/2}======${0 - startRa * cos(angle) - paragraph.height/2}");

      var out = 10;

      var pianyix = sin(angle) * (paragraph.width / 2 + out);
      var pianyiy = cos(angle) * (paragraph.height / 2 + out);
      var of = Offset(0 + startRa * sin(angle) - paragraph.width / 2 + pianyix,
          0 - startRa * cos(angle) - paragraph.height / 2 - pianyiy);
      var rect = Rect.fromCenter(
          center: Offset(0 + startRa * sin(angle) + pianyix - skewing,
              0 - startRa * cos(angle) - pianyiy),
          width: paragraph.width,
          height: paragraph.height);

      canvas.drawParagraph(paragraph, of);
      angle += delta;

      if (i == 0) {
        top = paragraph.height + out;
      }

      if (elementLength % 2 == 0) {
        if (i == elementLength / 2) {
          bottom = paragraph.height + out;
        }
      } else {
        if (i == elementLength ~/ 2) {
          bottom = paragraph.height + out;
        }
        if (i == elementLength ~/ 2 + 1) {
          if (bottom < paragraph.height) {
            bottom = paragraph.height + out;
          }
        }
      }

      node.add(rect);
    }

    // print("${node.length}");
    // node.forEach((element) {
    //   print("${element.left} . ${element.top} 11");
    // });

    WidgetsBinding.instance!.addPostFrameCallback((timeStamp) {
      _widthHeight.call(top, bottom);
    });

    //
    // for (int s = ring; s > 0; s--) {
    //   ///起始位置
    //   var startRa = innerRadius/ring * s - radarMap.radius * 0.05;
    //   Offset offset = Offset(-maxWidth/2, -startRa);
    //   // fontSize: textStyle!.fontSize ?? radarMap.radius * 0.16,
    //
    //
    //   final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle(
    //       textAlign: TextAlign.center,
    //       fontSize: line?.textFontSize ?? radarMap.radius * 0.1,
    //       fontWeight: FontWeight.normal));
    //   paragraphBuilder.pushStyle(ui.TextStyle(color: line?.textColor??Colors.black , textBaseline: ui.TextBaseline.alphabetic));
    //   paragraphBuilder.addText(lineText!.call(s,ring));
    //   var paragraph = paragraphBuilder.build();
    //   paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
    //   canvas.drawParagraph(paragraph, offset);
    //
  }

  //
  // /// 绘制文字
  // drawText(
  //   Canvas canvas,
  //   String text,
  //   Offset offset, {
  //   // Color color = Colors.black,
  //   double maxWith = 100,
  //   // double fontSize,
  //   String? fontFamily,
  //   TextAlign textAlign = TextAlign.center,
  //   FontWeight fontWeight = FontWeight.normal,
  // }) {
  //   var paragraphBuilder = ui.ParagraphBuilder(
  //     ui.ParagraphStyle(
  //       fontFamily: fontFamily,
  //       textAlign: textAlign,
  //       fontSize: textStyle!.fontSize ?? radarMap.radius * 0.16,
  //       fontWeight: fontWeight,
  //     ),
  //   );
  //   paragraphBuilder.pushStyle(ui.TextStyle(color: textStyle!.color ?? Colors.black, textBaseline: ui.TextBaseline.alphabetic));
  //   paragraphBuilder.addText(text);
  //   var paragraph = paragraphBuilder.build();
  //   paragraph.layout(ui.ParagraphConstraints(width: maxWith));
  //   canvas.drawParagraph(paragraph, Offset(offset.dx, offset.dy));
  // }

  //绘制文本弹框
  void drawInfoDialog(ui.Canvas canvas) {
    if (tab.index != null &&
        tab.x != null &&
        tab.y != null &&
        radarMap.dilog! &&
        dialogText != null) {
      List<LegendModel> legendModels =
          radarMap.legend.map((item) => item).toList();
      List<double> mapDataModels =
          radarMap.data.map((item) => item.data[tab.index!]).toList();
      // for(int i=0;i<radarMap.data.length;i++){
      //   legendModels.add(radarMap.legend[i]);
      //   mapDataModels.add(radarMap.data[i].data[tab.index!]);
      // }
      //

      IndicatorModel indicatorModel = radarMap.indicator[tab.index!];
      double maxWidth = radarMap.dialogModel?.maxWidth ?? 150.0;

      final paragraphBuilder = ui.ParagraphBuilder(ui.ParagraphStyle(
          textAlign: TextAlign.center,
          fontSize: radarMap.dialogModel?.textFontSize ?? 13,
          fontWeight: FontWeight.normal));
      paragraphBuilder.pushStyle(ui.TextStyle(
          color: Colors.white, textBaseline: ui.TextBaseline.alphabetic));

      paragraphBuilder.addText(
          dialogText!.call(indicatorModel, legendModels, mapDataModels));
      var paragraph = paragraphBuilder.build();
      paragraph.layout(ui.ParagraphConstraints(width: maxWidth));
      var bian = 6;
      var sanjiao = 10;
      var rectx = tab.x! - paragraph.width / 2;
      var recty = tab.y! +
          ((tab.y! > 0)
              ? -paragraph.height - sin(45) * sanjiao - bian
              : sin(45) * sanjiao + bian);

      var rect = RRect.fromLTRBR(rectx, recty - bian, paragraph.width + rectx,
          paragraph.height + recty + bian, const Radius.circular(15));
      canvas.drawRRect(rect, mDialogPaint);

      mDialogPath.moveTo(tab.x!, tab.y!);
      mDialogPath.lineTo(
          tab.x! - sanjiao, tab.y! + ((tab.y! > 0) ? -sanjiao : sanjiao));
      mDialogPath.lineTo(
          tab.x! + sanjiao, tab.y! + ((tab.y! > 0) ? -sanjiao : sanjiao));
      canvas.drawPath(mDialogPath, mDialogPaint);
      var of = Offset(rectx, recty);
      canvas.drawParagraph(paragraph, of);
      // var rect = Rect.fromCenter(center: Offset(tab.x! + ((tab.x!>0)?0:0), tab.y!), width: paragraph.width, height:paragraph.height);
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants