Repository: flutter
Issue: Flutter app is crash due to custom painter in ios but working fine in android using impeller 
Labels: waiting for customer response,in triage

### Steps to reproduce

flutter run 

### Expected results

app should be run fine on both platfrom

### Actual results

when I open a screen where Custom Painter uses the app, it crashes on IOS but working fine on android also no any logs if i run application from android studio but error showing in xcode  

### Code sample

<details open><summary>Code sample</summary>

```dart
class CurvedChartPainter extends CustomPainter {

  final List<Map<String, Map<String, dynamic>>> xValues;
  final List<Map<String, double>> yValues;
  final Color? color;
  final String currency;
  final double strokeWidth;
  final List<Color> gradientColors;
  final List<double> gradientStops;
  final List<Color> pathGradientColors;
  final List<double> pathGradientStops;
  final List<Offset> chartPoints = [];
  final TextStyle labelTextStyle;
  final Offset? selectedRecord;
  final Function(List<Offset> points) getPoints;
  final Function(Offset) onTap;

  // Constructor
  CurvedChartPainter({
    required this.xValues,
    required this.yValues,
    required this.strokeWidth,
    required this.pathGradientColors,
    required this.pathGradientStops,
    required this.currency,
    this.selectedRecord,
    this.color,
    required this.gradientColors,
    required this.gradientStops,
    this.labelTextStyle = const TextStyle(color: descriptionTextColor, fontSize: 12),
    required this.getPoints,
    required this.onTap,
  });

  // The paint method is called when the custom painter needs to paint
  @override
  void paint(Canvas canvas, Size size) {
    // Set up the paint for the chart line
    var paint = Paint();
    paint.color = color ?? const Color(0xFFF63E02);
    paint.style = PaintingStyle.stroke;
    paint.strokeWidth = strokeWidth;

    // Set up the paint for the chart fill
    var fillPaint = Paint();

    final dottedLinePaint = Paint()
      ..color = grey
      ..strokeWidth = 1.5;
    fillPaint.style = PaintingStyle.fill;

    final selectedDottedLinePaint = Paint()
      ..color = primaryColor
      ..strokeWidth = 2.5;
    fillPaint.style = PaintingStyle.fill;
    //
    // // point painter
    Paint paintCircle = Paint()..color = white;
    Paint paintBorder = Paint()
      ..color = primaryColor
      ..strokeWidth = 1.5
      ..style = PaintingStyle.stroke;

    // Create paths for the chart line and fill
    var path = Path();
    var fillPath = Path();
    var arrowPath = Path();

    var toolTipPainter = Paint();
    toolTipPainter.color = white;
    toolTipPainter.style = PaintingStyle.fill;
    toolTipPainter.strokeWidth = strokeWidth;

    var toolTipBorderPainter = Paint();
    toolTipBorderPainter.color = primaryColor;
    // toolTipBorderPainter.blendMode = ui.BlendMode.srcIn;
    toolTipBorderPainter.style = PaintingStyle.stroke;
    toolTipPainter.strokeWidth = strokeWidth;

    chartPoints.clear();
    // Check if there are enough values to draw the chart

    yValues.sort((a, b) => a.values.first.compareTo(b.values.first));
    // xValues.first.values.toList().sort((a, b) => a.values.first.compareTo(b.values.first));

    // draw y axis labels




    const double yItemHeight = 12;
    const double yItemHeightSpace = 5;
    double left = 0;
    double top  = 0;
    double maxyLabelTextWidth = 0;




    for (int i = 0; i < yValues.length ; i++) {
       double y = (size.height -8) * i / (yValues.length - 1) - 10;
       double labelValue = yValues.last.values.elementAt(0) *
           (yValues.length - i - 1) /
           (yValues.length - 1);
       var textPainter = TextPainter(
         text: TextSpan(
             text: labelValue.toStringAsFixed(0), style: labelTextStyle),
         textDirection: TextDirection.ltr,
       );
       textPainter.layout();
       textPainter.paint(
           canvas, Offset(5, y - ((textPainter.height / 2) -8 )));

       if(maxyLabelTextWidth < textPainter.width){
         maxyLabelTextWidth = textPainter.width;
       }

     }

    maxyLabelTextWidth += 5;

    // final int itemCount  =  ((size.height) / (yItemHeight + yItemHeightSpace)).floor();
    // final List<double> values  = createIntervals(yValues.first.values.first, yValues.last.values.first, itemCount).reversed.toList();
    // for(int i = 0; i <values.length; i++ ) {
    //
    //   var yTextPainter = TextPainter(
    //     text: TextSpan(text: values[i].toInt().toString(), style: kOutFitLight.copyWith(color: descriptionTextColor,fontSize: 10)),
    //     textDirection: TextDirection.ltr,
    //
    //   );
    //   yTextPainter.layout();
    //   yTextPainter.paint(canvas, ui.Offset(left, top));
    //   // canvas.drawRect(ui.Rect.fromLTWH(left, top, yTextPainter.width, yItemHeight), ui.Paint()
    //   //   ..color = Colors.red);
    //   top += yItemHeight + yItemHeightSpace;
    //   logDebug(yTextPainter.width.toString());
    //   if(yTextPainter.width > maxyLabelTextWidth){
    //     maxyLabelTextWidth = yTextPainter.width;
    //   }
    //
    // }

    logDebug(maxyLabelTextWidth.toString());
    if (xValues.length > 1 && yValues.isNotEmpty) {
      // Calculate some initial values
      final maxValue = yValues.last.values.reduce((value, element) => max(value, element));
      final firstValueHeight = size.height * (xValues.first.values.first['international_rate'] / maxValue);
      final firstY = size.height - strokeWidth - ((size.height - strokeWidth) * (xValues[0].values.elementAt(0)['international_rate'] / maxValue));

      // Initialize the paths with the first point
      path.moveTo(maxyLabelTextWidth + 5, size.height - firstValueHeight);
      fillPath.moveTo(maxyLabelTextWidth + 5, size.height);
      fillPath.lineTo(maxyLabelTextWidth + 5, size.height - firstValueHeight);
      chartPoints.add(Offset(maxyLabelTextWidth + 5, firstY.isNaN ? 0 : firstY));
      // Calculate the distance between each x value
      final itemXDistance = (size.width -left - maxyLabelTextWidth - 10  ) / (xValues.length - 1);

      // Loop through the x values and draw the chart line and fill

      for (var i = 1; i < xValues.length; i++) {
        final x = itemXDistance * i + (left + maxyLabelTextWidth + 10);
        final valueHeight = size.height - strokeWidth - ((size.height - strokeWidth) * (xValues[i].values.elementAt(0)['international_rate'] / maxValue));
        final previousValueHeight =
            size.height - strokeWidth - ((size.height - strokeWidth) * (xValues[i - 1].values.elementAt(0)['international_rate'] / maxValue));

        // Draw a quadratic bezier curve between each point
        path.quadraticBezierTo(
          x - (itemXDistance / 2) - (itemXDistance / 8),
          previousValueHeight,
          x - (itemXDistance / 2),
          valueHeight + ((previousValueHeight - valueHeight) / 2),
        );
        path.quadraticBezierTo(
          x - (itemXDistance / 2) + (itemXDistance / 8),
          valueHeight,
          x,
          valueHeight,
        );
// Add the current point to the chart points
        chartPoints.add(Offset(x, valueHeight.isNaN ? 0 : valueHeight));
        // Draw the fill path using the same quadratic bezier curves
        fillPath.quadraticBezierTo(
          x - (itemXDistance / 2) - (itemXDistance / 8),
          previousValueHeight,
          x - (itemXDistance / 2),
          valueHeight + ((previousValueHeight - valueHeight) / 2),
        );
        fillPath.quadraticBezierTo(
          x - (itemXDistance / 2) + (itemXDistance / 8),
          valueHeight,
          x,
          valueHeight,
        );
      }

      // if (myWeight.isFocusing) {

      //draw dotted lines

      for (int i = 0; i < chartPoints.length; i++) {
        double startY = -20;
        const dashHeight = 7, dashSpace = 5;
          if (i == chartPoints.length - 1) chartPoints[i] = chartPoints[i].translate(-10, 0);
          double opacityIncrement = 0.0;
        while (startY < size.height - 15) {
          if (startY >= 0 - 20 && startY <= size.height - size.height * 0.95) {
            // Apply gradient effect at the top and bottom
            // selectedDottedLinePaint.shader = LinearGradient(
            //   colors: [
            //     primaryColor.withOpacity(0), // fade into transparency
            //     primaryColor.withOpacity(min(1, 0.2 + opacityIncrement) ),
            //   ],
            //   stops: const [0.0,1],
            //   begin: Alignment.topRight,
            //   end: Alignment.bottomLeft,
            // ).createShader(Rect.fromLTRB(chartPoints[i].dx, startY, chartPoints[i].dx, startY + dashHeight));
            opacityIncrement +=0.2;
          } else {
            selectedDottedLinePaint.shader = null;
            selectedDottedLinePaint.color = primaryColor;
          }
          canvas.drawLine(Offset(chartPoints[i].dx, startY), Offset(chartPoints[i].dx, startY + dashHeight), selectedRecord != null && chartPoints[i].dx.toInt() > selectedRecord!.dx - 8 && chartPoints[i].dx.toInt() < selectedRecord!.dx + 8?  selectedDottedLinePaint : dottedLinePaint) ;
          startY += dashHeight + dashSpace;
        }
      }

      // canvas.drawLine(ui.Offset(50, size.height + 50), const ui.Offset(50, 52), ui.Paint()..color = Colors.red);
      // Close the fill path
      fillPath.lineTo(size.width, size.height);
      fillPath.close();
    }

    for (int i = 0; i < xValues.length; i++) {
      final width = i== 0 ? size.width -20 : size.width;
      double x = width * i / (xValues.length - 1);
      var textPainter = TextPainter(
        text:
        TextSpan(text: xValues[i].keys.elementAt(0), style: labelTextStyle),
        textDirection: TextDirection.ltr,
      );
      textPainter.layout();
      textPainter.paint(
          canvas,ui.Offset(chartPoints[i].dx - (maxyLabelTextWidth - (i == xValues.length -1 ? 0:2)),size.height.toDouble()));
    }

    // Create a gradient for the fill
    LinearGradient gradient = LinearGradient(
      colors: gradientColors,
      stops: gradientStops,
      begin: Alignment.topCenter,
      end: Alignment.bottomCenter,
    );

    LinearGradient pathGradient = LinearGradient(
      colors: pathGradientColors,
      stops: pathGradientStops,
      begin: Alignment.centerLeft,
      end: Alignment.centerRight,
    );
    Rect rect = Rect.fromLTWH(0, 0, size.width, size.height);
    fillPaint.shader = gradient.createShader(rect);
    paint.shader = pathGradient.createShader(rect);

    // Draw the fill path with the gradient
    canvas.drawPath(fillPath, fillPaint);

    // Draw the chart line
    canvas.drawPath(path, paint);

    path.close();
    fillPath.close();

    // chartPoints[chartPoints.length -1] = chartPoints[chartPoints.length -1].translate(-10, 0);
    // pass current points
    getPoints(chartPoints);

    // // Draw X axis labels
    // for (int i = 0; i < xValues.length; i++) {
    //   double x = (size.width + left + maxyLabelTextWidth + 5 ) * i / (xValues.length - 1);
    //   var textPainter = TextPainter(
    //     text: TextSpan(text: xValues[i].keys.elementAt(0), style: kOutFitLight.copyWith(color: descriptionTextColor,fontSize: 10)),
    //     textDirection: TextDirection.ltr,
    //
    //   );
    //   textPainter.layout();
    //   textPainter.paint(canvas, Offset( i == xValues.length -1 ? size.width - textPainter.width -8:x - textPainter.width / 2, size.height - 5));
    // } // // Draw X axis labels
    // for (int i = 0; i < xValues.length; i++) {
    //   double x = (size.width + left + maxyLabelTextWidth + 5 ) * i / (xValues.length - 1);
    //   var textPainter = TextPainter(
    //     text: TextSpan(text: xValues[i].keys.elementAt(0), style: kOutFitLight.copyWith(color: descriptionTextColor,fontSize: 10)),
    //     textDirection: TextDirection.ltr,
    //
    //   );
    //   textPainter.layout();
    //   textPainter.paint(canvas, Offset( i == xValues.length -1 ? size.width - textPainter.width -8:x - textPainter.width / 2, size.height - 5));
    // }


// Draw X axis labels
//     for (int i = 0; i < xValues.length; i++) {
//       double x = size.width * i / (xValues.length - 1);
//       var textPainter = TextPainter(
//         text:
//         TextSpan(text: xValues[i].keys.elementAt(0), style: labelTextStyle),
//         textDirection: TextDirection.ltr,
//       );
//       textPainter.layout();
//       textPainter.paint(
//           canvas, Offset(x - textPainter.width / 2, size.height + 2));
//     }
//
//     for (int i = 0; i < yValues.length; i++) {
//        double y = size.height * i / (yValues.length - 1);
//        double labelValue = yValues.last.values.elementAt(0) *
//            (yValues.length - i - 1) /
//            (yValues.length - 1);
//        var textPainter = TextPainter(
//          text: TextSpan(
//              text: labelValue.toStringAsFixed(0), style: labelTextStyle),
//          textDirection: TextDirection.ltr,
//        );
//        textPainter.layout();
//        textPainter.paint(
//            canvas, Offset(-textPainter.width - 2, y - textPainter.height / 2));
//      }

    if (selectedRecord != null) {
      logDebug(chartPoints.toString());

      for (int i = 0; i < chartPoints.length; i++) {
        logDebug(chartPoints.toString());
        if (chartPoints[i].dx.toInt() > selectedRecord!.dx - 8 && chartPoints[i].dx.toInt() < selectedRecord!.dx + 8) {
          logDebug('found record $selectedRecord');

          var path = Path();
          path.addOval(Rect.fromCircle(center: chartPoints[i], radius: 8));

          canvas.drawShadow(path, kDefaultIconDarkColor, 5, true);
          canvas.drawPath(path, paintCircle);
          canvas.drawCircle(chartPoints[i], 8, paintBorder);

          /// slab pain
          ///
          ///
          TextStyle slabStyle = kOutFitSemiBold.copyWith(
            color: descriptionTextColor,
            fontSize: 12,
            height: 1,
          );
          TextSpan slabSpan = TextSpan(
            text: 'Up to ${xValues[i].keys.first}',
            style: slabStyle.copyWith(
              height: 1,
            ),
          );
          final slabPainter = TextPainter(
            text: slabSpan,
            textDirection: TextDirection.ltr,
          );
          slabPainter.layout(
            minWidth: 0,
            maxWidth: size.width,
          );

          /// paint spending
          TextStyle spendingAmountStyle = kOutFitSemiBold.copyWith(
            color: primaryColor,
            fontSize: 14,
            height: 1,
          );
          TextSpan spendingAmountSpan = TextSpan(
            text: '${xValues[i].values.first['international_rate']} per KG',
            style: spendingAmountStyle.copyWith(
              height: 1,
            ),
          );
          final spendingPainter = TextPainter(
            text: spendingAmountSpan,
            textDirection: TextDirection.ltr,
          );
          spendingPainter.layout(
            minWidth: 0,
            maxWidth: size.width,
          );
          //
          // comparisonPainter.layout(
          //   minWidth: 0,
          //   maxWidth: size.width,
          // );
          //
          //
          // final comparisonXCenter = xValues[i].values.first['Diff_in_percentage'] == 'NaN' || xValues[i].values.first['Diff_in_percentage'] == 'Infinity'   ?chartPoints[i].dx + 12 + spendingPainter.width > size.width ?  chartPoints[i].dx - spendingPainter.width - 12 : chartPoints[i].dx + 12:chartPoints[i].dx + 12 + comparisonPainter.width > size.width || chartPoints[i].dx + 12 + spendingPainter.width > size.width
          //     ? chartPoints[i].dx - 12 - comparisonPainter.width
          //     : chartPoints[i].dx + 12;
          //
          // final comparisonYCenter =
          // chartPoints[i].dx + 8 + spendingPainter.width > size.width
          //     ? min(chartPoints[i].dy, chartPoints[i - 1].dy) - 30
          //     : min(chartPoints[i].dy, chartPoints[i + 1].dy) - 30;
          //
          final spendingYCenter = chartPoints[i].dy - 30;
          final spendingXCenter = chartPoints[i].dx;

          final backgroundRect = ui.Rect.fromLTWH(
               i ==0 ? chartPoints[i].dx + 8 :chartPoints[i].dx - (max(slabPainter.width, spendingPainter.width)  / 2),
              chartPoints[i].dy - slabPainter.height - spendingPainter.height - 25,
               max(spendingPainter.width, slabPainter.width) + 10,
              spendingPainter.height + slabPainter.height + 10
              // center: ui.Offset(spendingXCenter, spendingYCenter),
              // width: max(spendingPainter.width, spendingPainter.width) + 10,
              // height: spendingPainter.height + spendingPainter.height
          );

          final backgroundRectForSpaceIssues = ui.Rect.fromLTWH(
              size.width - backgroundRect.width - 8,
              chartPoints[i].dy - slabPainter.height - spendingPainter.height - 25,
              max(spendingPainter.width, slabPainter.width) + 10,
              spendingPainter.height + slabPainter.height + 10
            // center: ui.Offset(spendingXCenter, spendingYCenter),
            // width: max(spendingPainter.width, spendingPainter.width) + 10,
            // height: spendingPainter.height + spendingPainter.height
          );
          // canvas.drawRect(backgroundRect, toolTipPainter);
          final borderRect = ui.Rect.fromLTWH(
              chartPoints[i].dx - (max(slabPainter.width, spendingPainter.width)  / 2),
              chartPoints[i].dy - slabPainter.height - spendingPainter.height - 22,
              max(spendingPainter.width, slabPainter.width) + 10 ,
              spendingPainter.height + slabPainter.height + 10
            // center: ui.Offset(spendingXCenter, spendingYCenter),
            // width: max(spendingPainter.width, spendingPainter.width) + 10,
            // height: spendingPainter.height + spendingPainter.height
          );
          logDebug('${borderRect.left} ${borderRect.right} ${size.width}');
          canvas.drawRRect(ui.RRect.fromRectAndRadius(backgroundRect.right > size.width +  8 ?  backgroundRectForSpaceIssues: backgroundRect,const ui.Radius.circular(0)), toolTipPainter);
          spendingPainter.paint(canvas, Offset(backgroundRect.right > size.width +  8 ? backgroundRectForSpaceIssues.left + 5 : backgroundRect.left + 5, spendingYCenter));
          slabPainter.paint(canvas, Offset(backgroundRect.right > size.width +  8 ? backgroundRectForSpaceIssues.left + 5 :backgroundRect.left + 5, spendingYCenter - 20));
          //
          // if(xValues[i].values.first['Diff_in_percentage'] != 'NaN' && xValues[i].values.first['Diff_in_percentage'] != 'Infinity'  )comparisonPainter.paint(canvas, Offset(comparisonXCenter, comparisonYCenter));

          /// paint months
          // TextStyle textStyle = kSFMedium.copyWith(
          //   color: blue4.withOpacity(0.8),
          // );
          // TextSpan textSpan = TextSpan(
          //   text: xValues[i].keys.first,
          //   style: textStyle.copyWith(
          //     height: 1,
          //   ),
          // );
          // final textPainter = TextPainter(
          //   text: textSpan,
          //   textDirection: TextDirection.ltr,
          // );
          // textPainter.layout(
          //   minWidth: 0,
          //   maxWidth: size.width,
          // );

          // final xCenter = chartPoints[i].dx + 8 + textPainter.width > size.width ? chartPoints[i].dx - 8 - textPainter.width : chartPoints[i].dx + 8;
          // final yCenter = size.height +2;
          // chartPoints[i].dy > size.height * 0.99
          //     ? size.height - 30
          //     : chartPoints[i].dy > size.height * 0.9
          //         ? size.height - 4
          //         : size.height - 20;
          // final offset = Offset(xCenter, yCenter);
          // textPainter.paint(canvas, Offset(xCenter, yCenter));
        }
      }
    }
  }

  // Determine whether the chart should repaint
  @override
  bool shouldRepaint(CustomPainter oldDelegate) => oldDelegate != this;
}
```
</details>

here my chart painter code it's working fine by running  --no-enable-impeller on both android and ios 



### Screenshots or Video


![image](https://github.com/user-attachments/assets/f076a857-6564-41d3-a5f3-c10afbe58cbe)



### Logs

io.flutter.1.raster (9): EXC_BAD_ACCESS (code=1, address=0xfffffffffffffff0)

### Flutter Doctor output

<details open><summary>Doctor output</summary>

```console
[✓] Flutter (Channel stable, 3.27.1, on macOS 14.4.1 23E224 darwin-arm64, locale en-IN)
    • Flutter version 3.27.1 on channel stable at /Users/justcode-m1/Desktop/development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 17025dd882 (3 weeks ago), 2024-12-17 03:23:09 +0900
    • Engine revision cb4b5fff73
    • Dart version 3.6.0
    • DevTools version 2.40.2

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/justcode-m1/Library/Android/sdk
    • Platform android-35, build-tools 34.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11609105)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 15.3)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15E204a
    • CocoaPods version 1.13.0

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2024.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.10+0-17.0.10b1087.21-11609105)

[✓] IntelliJ IDEA Community Edition (version 2024.2.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
```

</details>