Skip to content

Setting onMarkerRender has conflict with zoom and scroll #461

Closed
@P-B1101

Description

@P-B1101

When I set zoomPanBehavior with autoScrollingDelta in primaryXAxis of 5 and if i have like 7 point to show and set onMarkerRender parameter, I got this error.

════════ Exception caught by rendering library ═════════════════════════════════
The following _CastError was thrown during paint():
Null check operator used on a null value
e:syncfusion_flutter_charts/…/common/marker.dart:337

This is my Code.

Widget _chart(
    MuscleProgressSuccessState progressState,
  ) =>
      (progressState.progress?.volumes.isEmpty ?? true)
          ? _emptyState
          : BlocBuilder<SettingsCubit, SettingsState>(
              builder: (context, mState) {
                final isDark = AppProvider.of(context).isDark;
                final gridColor = isDark
                    ? MyColors.darkChartGridLinesColor
                    : MyColors.chartGridLinesColor;
                final textColor = isDark
                    ? MyColors.darkChartTextColor
                    : MyColors.chartTextColor;
                final itemList = progressState.progress?.volumes ?? [];
                final items = itemList.sublist(
                  max(itemList.length - 7, 0),
                );
                final itemCounts = items.length;
                final feelings = progressState.progress?.feelings ?? [];
                return Container(
                  margin: const EdgeInsetsDirectional.only(
                    start: 16,
                    end: 24,
                    top: 24,
                    bottom: 16,
                  ),
                  width: double.infinity,
                  height: double.infinity,
                  child: SfCartesianChart(
                    key: UniqueKey(),
                    plotAreaBorderWidth: 0,
                    axisLabelFormatter: (details) {
                      final isY =
                          details.orientation == AxisOrientation.vertical;
                      final yValue = details.value
                          .toDouble()
                          .getWeight(mState.settings.metrics)
                          .toDouble()
                          .toNum;
                      return ChartAxisLabel(
                        isY ? yValue : details.actualText,
                        TextStyle(
                          color: textColor,
                          fontFamily: Fonts.alvarEssential,
                          fontSize: 12,
                          fontWeight: Fonts.regular,
                        ),
                      );
                    },
                    selectionType: SelectionType.point,
                    zoomPanBehavior: ZoomPanBehavior(
                      enablePanning: true,
                      enablePinching: true,
                      zoomMode: ZoomMode.x,
                      enableDoubleTapZooming: true,
                    ),
                    primaryXAxis: NumericAxis(
                      autoScrollingDelta: 5,
                      zoomFactor: .5,
                      zoomPosition: .5,
                      enableAutoIntervalOnZooming: true,
                      maximum: itemCounts.toDouble(),
                      axisLine: AxisLine(
                        width: 1,
                        color: gridColor,
                      ),
                      interval: 1,
                      majorGridLines: MajorGridLines(
                        width: 1,
                        color: gridColor,
                      ),
                      majorTickLines: MajorTickLines(
                        color: AppProvider.of(context).isDark
                            ? Theme.of(context).textTheme.bodyText1?.color
                            : MyColors.subtitleColor2,
                        size: 4,
                        width: 4,
                      ),
                    ),
                    primaryYAxis: NumericAxis(
                      isVisible: true,
                      majorGridLines: MajorGridLines(
                        color: gridColor,
                        width: 1,
                      ),
                      axisLine: AxisLine(
                        width: 1,
                        color: gridColor,
                      ),
                      majorTickLines: MajorTickLines(
                        color: AppProvider.of(context).isDark
                            ? Theme.of(context).textTheme.bodyText1?.color
                            : MyColors.subtitleColor2,
                        size: 0,
                        width: 0,
                      ),
                      minimum: (items.minValue * .8).roundToDouble(),
                      maximum: (items.maxValue * 1.25).roundToDouble(),
                    ),
                    trackballBehavior: TrackballBehavior(
                      enable: true,
                      markerSettings: const TrackballMarkerSettings(
                        color: Colors.transparent,
                        borderColor: Colors.transparent,
                        borderWidth: 5,
                        height: 11,
                        markerVisibility: TrackballVisibilityMode.visible,
                        width: 11,
                        shape: DataMarkerType.circle,
                      ),
                      activationMode: ActivationMode.singleTap,
                      shouldAlwaysShow: true,
                      lineDashArray: const [5, 5],
                      lineType: TrackballLineType.vertical,
                      lineWidth: 1,
                      tooltipSettings: InteractiveTooltip(
                        color: _mainColor(context),
                        borderColor: _mainColor(context),
                      ),
                      lineColor: _mainColor(context),
                      builder: (context, details) {
                        return BlocBuilder<WeightChartTouchCubit,
                            WeightChartTouchState>(
                          builder: (context, state) {
                            final intData = details.point?.y as int;
                            final initialIndex = details.point?.x;
                            final String feelingStr;
                            if (initialIndex == null ||
                                initialIndex is! int ||
                                feelings.isEmpty) {
                              feelingStr = '';
                            } else {
                              int index =
                                  min(feelings.length, initialIndex - 1);
                              index = max(0, index);
                              final feeling = feelings[index];
                              feelingStr = _titles(context)[feeling];
                            }

                            return Container(
                              padding: const EdgeInsets.only(
                                left: 12,
                                right: 12,
                                top: 4,
                                bottom: 4,
                              ),
                              decoration: BoxDecoration(
                                borderRadius: BorderRadius.circular(5),
                                border: Border.all(
                                  width: 1,
                                  color: _mainColor(context),
                                ),
                                color: Theme.of(context).cardColor,
                              ),
                              child: Text.rich(
                                TextSpan(
                                  style: TextStyle(
                                    fontFamily: Fonts.alvarEssential,
                                    fontWeight: Fonts.regular,
                                    color: AppProvider.of(context).isDark
                                        ? Theme.of(context)
                                            .textTheme
                                            .bodyText1
                                            ?.color
                                        : MyColors.subtitleColor2,
                                  ),
                                  children: [
                                    if (feelingStr.isNotEmpty)
                                      TextSpan(
                                        text: '$feelingStr\n',
                                        style: const TextStyle(
                                          fontSize: 13,
                                        ),
                                      ),
                                    TextSpan(
                                      text: intData
                                          .toDouble()
                                          .getWeight(mState.settings.metrics)
                                          .toDouble()
                                          .toNum,
                                      style: const TextStyle(
                                        fontSize: 15,
                                      ),
                                    ),
                                  ],
                                ),
                              ),
                            );
                          },
                        );
                      },
                      tooltipDisplayMode: TrackballDisplayMode.nearestPoint,
                    ),
                    tooltipBehavior: TooltipBehavior(
                      enable: false,
                    ),
                    onMarkerRender: (args) {
                      final initialIndex = args.pointIndex ?? -1;
                      final int feelingIndex;
                      if (feelings.isEmpty) {
                        feelingIndex = -1;
                      } else {
                        int index = min(feelings.length, initialIndex);
                        index = max(0, index);
                        final feeling = feelings[index];
                        feelingIndex = feeling;
                      }
                      if (feelingIndex >= 1 && feelingIndex <= 5) {
                        args.color = _getMarkerColor(context, feelingIndex);
                      }
                    },
                    series: [
                      AreaSeries<int, int>(
                        enableTooltip: true,
                        markerSettings: MarkerSettings(
                          isVisible: true,
                          color: _mainColor(context),
                          borderColor: isDark
                              ? Theme.of(context).textTheme.bodyText1?.color
                              : MyColors.subtitleColor2,
                          borderWidth: 3,
                          height: 14,
                          width: 14,
                          shape: DataMarkerType.circle,
                        ),
                        dataSource: items,
                        xValueMapper: (int weight, _) => _ + 1,
                        yValueMapper: (int weight, _) => weight,
                        borderDrawMode: BorderDrawMode.top,
                        borderWidth: 4,
                        gradient: LinearGradient(
                          colors: [
                            _mainColor(context).withOpacity(.8),
                            _mainColor(context).withOpacity(0)
                          ],
                          begin: Alignment.topCenter,
                          end: Alignment.bottomCenter,
                          stops: const [.25, 1],
                        ),
                        borderColor: _mainColor(context),
                      ),
                    ],
                  ),
                );
              },
            );

  Widget get _emptyState => Builder(
        builder: (context) {
          final isDark = AppProvider.of(context).isDark;
          final gridColor = isDark
              ? MyColors.darkChartGridLinesColor
              : MyColors.chartGridLinesColor;
          final textColor =
              isDark ? MyColors.darkChartTextColor : MyColors.chartTextColor;
          final items = [0, 0, 0, 0, 0, 0, 0];
          final itemCounts = items.length;
          return Container(
            margin: const EdgeInsetsDirectional.only(
              start: 16,
              end: 24,
              top: 32,
              bottom: 16,
            ),
            width: double.infinity,
            height: double.infinity,
            child: Blur(
              blur: 2,
              alignment: Alignment.center,
              blurColor: Theme.of(context).backgroundColor,
              overlay: Builder(
                builder: (context) => Center(
                  child: MyText(
                    text: AppLocalizations.of(context)!.empty_chart_data,
                    fontSize: 20,
                    fontWeight: Fonts.medium,
                  ),
                ),
              ),
              child: SfCartesianChart(
                plotAreaBorderWidth: 0,
                axisLabelFormatter: (details) => ChartAxisLabel(
                  details.actualText,
                  TextStyle(
                    color: textColor,
                    fontFamily: Fonts.alvarEssential,
                    fontSize: 12,
                    fontWeight: Fonts.regular,
                  ),
                ),
                selectionType: SelectionType.point,
                primaryXAxis: NumericAxis(
                  maximum: itemCounts + _xInterval * .2,
                  axisLine: AxisLine(
                    width: 1,
                    color: gridColor,
                  ),
                  interval: 1,
                  majorGridLines: MajorGridLines(
                    width: 1,
                    color: gridColor,
                  ),
                  majorTickLines: MajorTickLines(
                    color: AppProvider.of(context).isDark
                        ? Theme.of(context).textTheme.bodyText1?.color
                        : MyColors.subtitleColor2,
                    size: 4,
                    width: 4,
                  ),
                ),
                primaryYAxis: NumericAxis(
                  isVisible: true,
                  majorGridLines: MajorGridLines(
                    color: gridColor,
                    width: 1,
                  ),
                  axisLine: AxisLine(
                    width: 1,
                    color: gridColor,
                  ),
                  majorTickLines: MajorTickLines(
                    color: AppProvider.of(context).isDark
                        ? Theme.of(context).textTheme.bodyText1?.color
                        : MyColors.subtitleColor2,
                    size: 0,
                    width: 0,
                  ),
                  minimum: (items.minValue * .8).roundToDouble(),
                  maximum: (items.maxValue * 1.25).roundToDouble(),
                ),
                trackballBehavior: TrackballBehavior(
                  enable: true,
                  activationMode: ActivationMode.singleTap,
                  shouldAlwaysShow: true,
                  lineDashArray: const [5, 5],
                  lineType: TrackballLineType.vertical,
                  lineWidth: 1,
                  tooltipSettings: InteractiveTooltip(
                    color: _mainColor(context),
                    borderColor: _mainColor(context),
                  ),
                  lineColor: _mainColor(context),
                  builder: (context, details) {
                    return BlocBuilder<SettingsCubit, SettingsState>(
                      builder: (context, mState) => BlocBuilder<
                          WeightChartTouchCubit, WeightChartTouchState>(
                        builder: (context, state) {
                          final intData = details.point?.y as int;
                          return Container(
                            padding: const EdgeInsets.only(
                              left: 12,
                              right: 12,
                              top: 4,
                              bottom: 4,
                            ),
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(5),
                              border: Border.all(
                                width: 1,
                                color: _mainColor(context),
                              ),
                              color: Theme.of(context).cardColor,
                            ),
                            child: Text.rich(
                              TextSpan(
                                style: TextStyle(
                                  fontFamily: Fonts.alvarEssential,
                                  fontWeight: Fonts.regular,
                                  color: AppProvider.of(context).isDark
                                      ? Theme.of(context)
                                          .textTheme
                                          .bodyText1
                                          ?.color
                                      : MyColors.subtitleColor2,
                                ),
                                children: [
                                  TextSpan(
                                    text: intData
                                        .toDouble()
                                        .getWeight(mState.settings.metrics)
                                        .toDouble()
                                        .toNum,
                                    style: const TextStyle(
                                      fontSize: 15,
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          );
                        },
                      ),
                    );
                  },
                  tooltipDisplayMode: TrackballDisplayMode.nearestPoint,
                ),
                tooltipBehavior: TooltipBehavior(
                  enable: false,
                ),
                series: [
                  AreaSeries<int, int>(
                    dataSource: items,
                    xValueMapper: (int weight, _) => _ + 1,
                    yValueMapper: (int weight, _) => weight,
                  ),
                ],
              ),
            ),
          );
        },
      );

  double get _xInterval => 1;

  Color _mainColor(BuildContext context) => AppProvider.of(context).isDark
      ? MyColors.darkMentalColor
      : MyColors.mentalColor;

  Color? _getMarkerColor(BuildContext context, int index) {
    switch (index) {
      case 0:
        return AppProvider.of(context).isDark
            ? MyColors.darkVeryEasy
            : MyColors.veryEasy;
      case 1:
        return AppProvider.of(context).isDark
            ? MyColors.darkEasy
            : MyColors.easy;
      case 2:
        return AppProvider.of(context).isDark
            ? MyColors.darkModerate
            : MyColors.moderate;
      case 3:
        return AppProvider.of(context).isDark
            ? MyColors.darkHard
            : MyColors.hard;
      case 4:
        return AppProvider.of(context).isDark
            ? MyColors.darkVeryHard
            : MyColors.veryHard;
      default:
        return AppProvider.of(context).isDark
            ? Theme.of(context).textTheme.bodyText1?.color
            : MyColors.subtitleColor2;
    }
  }

  List<String> _titles(BuildContext context) => [
        AppLocalizations.of(context)!.very_easy_label,
        AppLocalizations.of(context)!.easy_label,
        AppLocalizations.of(context)!.moderate_label,
        AppLocalizations.of(context)!.hard_label,
        AppLocalizations.of(context)!.very_hard_label,
      ];

Full Error Log

#15 SchedulerBinding._invokeFrameCallback
package:flutter/…/scheduler/binding.dart:1143
#16 SchedulerBinding.handleDrawFrame
package:flutter/…/scheduler/binding.dart:1080
#17 SchedulerBinding._handleDrawFrame
package:flutter/…/scheduler/binding.dart:996
#21 _invoke (dart:ui/hooks.dart:166:10)
#22 PlatformDispatcher._drawFrame (dart:ui/platform_dispatcher.dart:270:5)
#23 _drawFrame (dart:ui/hooks.dart:129:31)
(elided 3 frames from dart:async)
The following RenderObject was being processed when the exception was fired: RenderCustomPaint#1d340 relayoutBoundary=up2
RenderObject: RenderCustomPaint#1d340 relayoutBoundary=up2
parentData: (can use size)
constraints: BoxConstraints(0.0<=w<=351.4, 0.0<=h<=330.0)
size: Size(0.0, 0.0)
════════════════════════════════════════════════════════════════════════════════

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions