Skip to content

Labels disappearing randomly #2309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
KrishnaPras4dGandrath opened this issue Mar 17, 2025 · 4 comments
Closed

Labels disappearing randomly #2309

KrishnaPras4dGandrath opened this issue Mar 17, 2025 · 4 comments
Labels
charts Charts component solved Solved the query using existing solutions

Comments

@KrishnaPras4dGandrath
Copy link

KrishnaPras4dGandrath commented Mar 17, 2025

Bug description

I'm using dynamic graph where user will be able to select y axis and x axes values , and graph changes based on selected x-axis, y-axis and secondary y-axis.When axes are changed labels are showing while the graph is being animated but when animation is complete, the labels are disappearing.

Steps to reproduce

code

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:syncfusion_flutter_charts/charts.dart';

void main() {
  runApp(const ProviderScope(
    child: MaterialApp(
      home: GraphSampleView(),
    ),
  ));
}

// Constants
const kClearOption = 'CLEAR';

const kAllowedXAxis = [
  'Category A',
  'Category B',
  'Category C',
];

const kAllowedYAxes = [
  'Value 1',
  'Value 2',
  'Value 3',
  'Value 4',
];

const kAxisTitles = {
  'Category A': 'Category A (Long Title)',
  'Category B': 'Category B (Medium)',
  'Category C': 'Category C',
  'Value 1': 'Value 1 (Numbers)',
  'Value 2': 'Value 2 (Percentages)',
  'Value 3': 'Value 3 (Counts)',
  'Value 4': 'Value 4 (Ratios)',
  'CLEAR': 'Clear Selection',
};

const kPrimaryAxisColor = Colors.blue;
final kSecondaryAxisColor = Colors.red.shade400;

// Controller
final graphSampleProvider =
    ChangeNotifierProvider((ref) => GraphSampleController());

class GraphSampleController extends ChangeNotifier {
  String selectedXAxis = kAllowedXAxis[0];
  String selectedY1Axis = kAllowedYAxes[0];
  String? selectedY2Axis = kAllowedYAxes[1];
  bool graphRefreshIndicator = false;

  // Static data for testing
  final List<Map<String, dynamic>> _sampleData = [
    {
      'label': 'Very Long Label That Should Be Truncated',
      'value1': 100,
      'value2': 45.5,
      'value3': 200,
      'value4': 0.75,
    },
    {
      'label': 'Medium Label',
      'value1': 75,
      'value2': 60.2,
      'value3': 150,
      'value4': 0.85,
    },
    {
      'label': 'Short',
      'value1': 50,
      'value2': 30.8,
      'value3': 100,
      'value4': 0.45,
    },
    {
      'label': 'Test Label 1234',
      'value1': 125,
      'value2': 80.0,
      'value3': 300,
      'value4': 0.95,
    },
    {
      'label': 'Another Long Label For Testing',
      'value1': 90,
      'value2': 55.5,
      'value3': 180,
      'value4': 0.65,
    },
  ];

  List<Map<String, dynamic>> get y1DataSource {
    String valueKey = _getValueKey(selectedY1Axis);
    return _sampleData
        .map((item) => {
              'label': item['label'],
              'value': item[valueKey],
            })
        .toList();
  }

  List<Map<String, dynamic>> get y2DataSource {
    if (selectedY2Axis == null) return [];
    String valueKey = _getValueKey(selectedY2Axis!);
    return _sampleData
        .map((item) => {
              'label': item['label'],
              'value': item[valueKey],
            })
        .toList();
  }

  String _getValueKey(String axis) {
    switch (axis) {
      case 'Value 1':
        return 'value1';
      case 'Value 2':
        return 'value2';
      case 'Value 3':
        return 'value3';
      case 'Value 4':
        return 'value4';
      default:
        return 'value1';
    }
  }

  void onXAxisChanged(String value) {
    selectedXAxis = value;
    _refreshGraph();
  }

  void onY1AxisChanged(String value) {
    if (value == selectedY2Axis) {
      selectedY2Axis = selectedY1Axis;
    }
    selectedY1Axis = value;
    _refreshGraph();
  }

  void onY2AxisChanged(String value) {
    selectedY2Axis = value;
    _refreshGraph();
  }

  void resetY2Axis() {
    selectedY2Axis = null;
    _refreshGraph();
  }

  void _refreshGraph() {
    graphRefreshIndicator = !graphRefreshIndicator;
    notifyListeners();
  }
}

// View
class GraphSampleView extends ConsumerStatefulWidget {
  const GraphSampleView({super.key});

  @override
  ConsumerState<GraphSampleView> createState() => _GraphSampleViewState();
}

class _GraphSampleViewState extends ConsumerState<GraphSampleView> {
  late final controller = ref.read(graphSampleProvider);
  final _tooltipBehavior = TooltipBehavior(enable: true);

  @override
  Widget build(BuildContext context) {
    ref.watch(graphSampleProvider.select((v) => v.graphRefreshIndicator));
    ref.watch(graphSampleProvider
        .select((v) => {v.selectedXAxis, v.selectedY1Axis, v.selectedY2Axis}));

    return Scaffold(
      appBar: AppBar(title: const Text('Graph Sample')),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        children: [
          SizedBox(
            height: 40.0,
            child: Row(
              children: [
                const SizedBox(width: 16),
                axisDropdown(
                  label: 'X Axis',
                  options: kAllowedXAxis,
                  selected: controller.selectedXAxis,
                  onChanged: controller.onXAxisChanged,
                ),
                const SizedBox(width: 16),
                axisDropdown(
                  label: 'Y Axis I',
                  options: [...kAllowedYAxes]
                    ..remove(controller.selectedY2Axis),
                  selected: controller.selectedY1Axis,
                  onChanged: controller.onY1AxisChanged,
                ),
                const SizedBox(width: 16),
                axisDropdown(
                  label: 'Y Axis II',
                  options: [...kAllowedYAxes]
                    ..remove(controller.selectedY1Axis)
                    ..add(kClearOption),
                  selected: controller.selectedY2Axis,
                  onChanged: controller.onY2AxisChanged,
                  onReset: controller.resetY2Axis,
                ),
              ],
            ),
          ),
          Expanded(child: _buildGraph()),
        ],
      ),
    );
  }

  Widget _buildGraph() {
    return GestureDetector(
      onTap: () => _tooltipBehavior.hide(),
      child: Container(
        padding: const EdgeInsets.all(20.0),
        child: SfCartesianChart(
          tooltipBehavior: TooltipBehavior(
            enable: true,
            activationMode: ActivationMode.singleTap,
            animationDuration: 200,
            decimalPlaces: 3,
          ),
          primaryXAxis: CategoryAxis(
            name: 'primaryXAxis',
            isVisible: true,
            labelRotation: controller.selectedXAxis == 'Category A' ? 270 : 0,
            majorTickLines: const MajorTickLines(size: 0),
            majorGridLines: const MajorGridLines(width: 0),
            labelStyle: const TextStyle(fontSize: 10.0),
            interval: 1,
            maximumLabelWidth: 500.0,
            labelsExtent: 80,
            axisLabelFormatter: (axisLabelRenderArgs) {
              String text = axisLabelRenderArgs.text;
              return ChartAxisLabel(
                text,
                const TextStyle(fontSize: 10.0, overflow: TextOverflow.visible),
              );
            },
            interactiveTooltip: const InteractiveTooltip(enable: true),
            title: AxisTitle(text: kAxisTitles[controller.selectedXAxis]),
          ),
          primaryYAxis: NumericAxis(
            name: 'primaryYAxis',
            maximumLabelWidth: 300.0,
            labelIntersectAction: AxisLabelIntersectAction.trim,
            isVisible: true,
            title: AxisTitle(text: kAxisTitles[controller.selectedY1Axis]),
            majorTickLines: const MajorTickLines(size: 0),
            majorGridLines: MajorGridLines(
              width: 0.5,
              color: Colors.grey.withOpacity(0.4),
              dashArray: const [3, 3],
            ),
          ),
          axes: [
            if (controller.selectedY2Axis != null)
              NumericAxis(
                name: 'secondaryYAxis',
                isVisible: true,
                maximumLabelWidth: 300.0,
                anchorRangeToVisiblePoints: true,
                labelIntersectAction: AxisLabelIntersectAction.trim,
                decimalPlaces: 3,
                opposedPosition: true,
                majorTickLines: const MajorTickLines(size: 0),
                majorGridLines: MajorGridLines(
                  width: 0.5,
                  color: Colors.red.withOpacity(0.4),
                  dashArray: const [0, 3],
                ),
                labelStyle: TextStyle(color: kSecondaryAxisColor),
                title: AxisTitle(
                  text: kAxisTitles[controller.selectedY2Axis],
                  textStyle: TextStyle(color: kSecondaryAxisColor),
                ),
              ),
          ],
          isTransposed: true,
          series: [
            BarSeries<Map<String, dynamic>, String>(
              yAxisName: 'primaryYAxis',
              animationDuration: 400,
              name: kAxisTitles[controller.selectedY1Axis],
              dataSource: controller.y1DataSource,
              xValueMapper: (data, _) => data['label'] as String,
              yValueMapper: (data, _) => data['value'] as num,
              enableTooltip: true,
              color: kPrimaryAxisColor,
              width: 0.4,
            ),
            if (controller.selectedY2Axis != null)
              LineSeries<Map<String, dynamic>, String>(
                yAxisName: 'secondaryYAxis',
                animationDuration: 400,
                name: kAxisTitles[controller.selectedY2Axis],
                dataSource: controller.y2DataSource,
                xValueMapper: (data, _) => data['label'] as String,
                yValueMapper: (data, _) => data['value'] as num? ?? 0,
                enableTooltip: true,
                markerSettings: MarkerSettings(
                  isVisible: true,
                  borderWidth: 0,
                  color: kSecondaryAxisColor,
                  height: 6.0,
                  width: 6.0,
                ),
                color: kSecondaryAxisColor,
              ),
          ],
        ),
      ),
    );
  }

  Widget axisDropdown({
    required String label,
    required List<String> options,
    required String? selected,
    required void Function(String) onChanged,
    VoidCallback? onReset,
  }) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(label, style: const TextStyle(fontSize: 11.0)),
        const SizedBox(width: 8),
        DecoratedBox(
          decoration: BoxDecoration(
            color: Colors.grey[100],
            borderRadius: BorderRadius.circular(8),
            boxShadow: const [
              BoxShadow(
                color: Colors.black12,
                blurRadius: 0,
                offset: Offset(0, 0),
              ),
            ],
          ),
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 8.0),
            child: DropdownButtonHideUnderline(
              child: DropdownButton<String>(
                hint: const Text('-- Select --',
                    style: TextStyle(fontSize: 11.0)),
                value: selected,
                menuMaxHeight: 200.0,
                items: options
                    .map((e) => DropdownMenuItem<String>(
                          value: e,
                          child: Text(
                            kAxisTitles[e] ?? '-',
                            style: const TextStyle(fontSize: 11.0),
                          ),
                        ))
                    .toList(),
                onChanged: (value) => value != null
                    ? value == kClearOption
                        ? onReset?.call()
                        : onChanged(value)
                    : null,
              ),
            ),
          ),
        ),
      ],
    );
  }
}

Screenshots or Video

Screenshots / Video demonstration
Screen.Recording.2025-03-17.at.9.49.06.PM.mov

Stack Traces

Stack Traces
[Add the Stack Traces here]

On which target platforms have you observed this bug?

Web

Flutter Doctor output

Doctor output
[Add your output here]
@VijayakumarMariappan VijayakumarMariappan added charts Charts component open Open labels Mar 18, 2025
@PreethikaSelvam
Copy link
Contributor

Hi @KrishnaPras4dGandrath,

We can replicate this issue and already we have logged a bug report for this issue at our end. Unfortunately, we were unable to resolve this issue on our end due to a framework-level issue. The reason we are facing this issue is that we made architectural changes to the Chart widget to improve the widget loading performance on Volume 4, 2023 release. In doing so, we placed the axis on top of the series for behavioral improvement, but this caused issues in Web HTML rendering. We have created an issue report for the framework, and they have acknowledged it.
We are continuously following the issue and will update you once it has been fixed. We have shared the issue link below for your reference.

Framework issue: flutter/flutter#151650

Regards,
Preethika Selvam.

@KrishnaPras4dGandrath
Copy link
Author

KrishnaPras4dGandrath commented Mar 24, 2025 via email

@PreethikaSelvam
Copy link
Contributor

Hi @KrishnaPras4dGandrath,

We would like to inform you that the HTML web renderer has been deprecated starting from Flutter 3.24.0, and we recommend upgrading to Flutter 3.27.0 or later to take advantage of the WebAssembly (WASM) for improved performance and compatibility.

We have provided WebAssembly (WASM) support for our Charts widget starting from version 28.1.33 onwards, and we suggest using version 28.1.33 or later to overcome this issue.

You can refer to the changelog for version 28.1.33 here: https://pub.dev/packages/syncfusion_flutter_charts/versions/28.1.33/changelog

HTML deprecation details: ☂️ Intent to deprecate and remove the HTML renderer in Flutter Web · Issue #145954 · flutter/flutter

Please feel free to reach out if you need any further assistance, we're happy to help!

Best regards,
Preethika Selvam.

@Saravanan-Madhesh Saravanan-Madhesh added waiting for customer response Cannot make further progress until the customer responds. and removed open Open labels Apr 9, 2025
@LavanyaGowtham2021 LavanyaGowtham2021 added solved Solved the query using existing solutions and removed waiting for customer response Cannot make further progress until the customer responds. labels Apr 24, 2025
@LavanyaGowtham2021
Copy link
Collaborator

Please reopen this ticket, if you need further assistance with this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
charts Charts component solved Solved the query using existing solutions
Projects
None yet
Development

No branches or pull requests

5 participants