Skip to content
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

Labels disappearing randomly #2309

Open
KrishnaPras4dGandrath opened this issue Mar 17, 2025 · 0 comments
Open

Labels disappearing randomly #2309

KrishnaPras4dGandrath opened this issue Mar 17, 2025 · 0 comments

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]
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

No branches or pull requests

1 participant