flutterdartfl-chart

fl_chart scatter chart touch tooltip disappears when holding


I used the sample code from the github and only changed 1 variable: the handleBuiltInTouches to true, I expect it to have the tooltip pop up when I hold it, but it popped up for a splitsecond before disappearing.

also when I set handleBuiltInTouches to true, even when showingtooltipIndicators has selected all spots, none of the spots have the tooltip popup.

class ScatterChartSample2 extends StatefulWidget {
  const ScatterChartSample2({Key? key}) : super(key: key);

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

class _ScatterChartSample2State extends State {
  int touchedIndex = -1;

  Color greyColor = Colors.grey;

  List<int> selectedSpots = [];

  @override
  Widget build(BuildContext context) {
    return AspectRatio(
      aspectRatio: 1,
      child: Card(
        color: const Color(0xff222222),
        child: fl.ScatterChart(
          fl.ScatterChartData(
            scatterSpots: [
              fl.ScatterSpot(
                4,
                4,
                color: selectedSpots.contains(0) ? Colors.green : greyColor,
              ),
              fl.ScatterSpot(
                2,
                5,
                color: selectedSpots.contains(1) ? Colors.yellow : greyColor,
                radius: 12,
              ),
              fl.ScatterSpot(
                4,
                5,
                color:
                    selectedSpots.contains(2) ? Colors.purpleAccent : greyColor,
                radius: 8,
              ),
              fl.ScatterSpot(
                8,
                6,
                color: selectedSpots.contains(3) ? Colors.orange : greyColor,
                radius: 20,
              ),
              fl.ScatterSpot(
                5,
                7,
                color: selectedSpots.contains(4) ? Colors.brown : greyColor,
                radius: 14,
              ),
              fl.ScatterSpot(
                7,
                2,
                color: selectedSpots.contains(5)
                    ? Colors.lightGreenAccent
                    : greyColor,
                radius: 18,
              ),
              fl.ScatterSpot(
                3,
                2,
                color: selectedSpots.contains(6) ? Colors.red : greyColor,
                radius: 36,
              ),
              fl.ScatterSpot(
                2,
                8,
                color:
                    selectedSpots.contains(7) ? Colors.tealAccent : greyColor,
                radius: 22,
              ),
            ],
            minX: 0,
            maxX: 10,
            minY: 0,
            maxY: 10,
            borderData: fl.FlBorderData(
              show: false,
            ),
            gridData: fl.FlGridData(
              show: true,
              drawHorizontalLine: true,
              checkToShowHorizontalLine: (value) => true,
              getDrawingHorizontalLine: (value) =>
                  fl.FlLine(color: Colors.white.withOpacity(0.1)),
              drawVerticalLine: true,
              checkToShowVerticalLine: (value) => true,
              getDrawingVerticalLine: (value) =>
                  fl.FlLine(color: Colors.white.withOpacity(0.1)),
            ),
            titlesData: fl.FlTitlesData(
              show: false,
            ),
            showingTooltipIndicators: selectedSpots,
            scatterTouchData: fl.ScatterTouchData(
              enabled: true,
              handleBuiltInTouches: true,
              touchTooltipData: fl.ScatterTouchTooltipData(
                tooltipBgColor: Colors.black,
                getTooltipItems: (fl.ScatterSpot touchedBarSpot) {
                  return fl.ScatterTooltipItem(
                    'X: ',
                    textStyle: TextStyle(
                      height: 1.2,
                      color: Colors.grey[100],
                      fontStyle: FontStyle.italic,
                    ),
                    bottomMargin: 10,
                    children: [
                      TextSpan(
                        text: '${touchedBarSpot.x.toInt()} \n',
                        style: const TextStyle(
                          color: Colors.white,
                          fontStyle: FontStyle.normal,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      TextSpan(
                        text: 'Y: ',
                        style: TextStyle(
                          height: 1.2,
                          color: Colors.grey[100],
                          fontStyle: FontStyle.italic,
                        ),
                      ),
                      TextSpan(
                        text: touchedBarSpot.y.toInt().toString(),
                        style: const TextStyle(
                          color: Colors.white,
                          fontStyle: FontStyle.normal,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  );
                },
              ),
              touchCallback: (fl.FlTouchEvent event,
                  fl.ScatterTouchResponse? touchResponse) {
                if (touchResponse == null ||
                    touchResponse.touchedSpot == null) {
                  return;
                }
                if (event is fl.FlTapUpEvent) {
                  final sectionIndex = touchResponse.touchedSpot!.spotIndex;
                  setState(() {
                    if (selectedSpots.contains(sectionIndex)) {
                      selectedSpots.remove(sectionIndex);
                    } else {
                      selectedSpots.add(sectionIndex);
                    }
                  });
                }
              },
            ),
          ),
        ),
      ),
    );
  }
}


Solution

  • The fl will give the return fl.FlTapDownEvent and fl.FlLongPressEnd if you give long press.

    Set fl condition like this:

    // Show tooltip if tap down detected
    if (event is fl.FlTapDownEvent) {
         final sectionIndex = touchResponse.touchedSpot!.spotIndex;
         setState(() {
            selectedSpots.add(sectionIndex);
         });
    // Hide/clear tooltip if long press was ended or tap up detected
    }else if(event is fl.FlLongPressEnd || event is fl.FlTapUpEvent){
         setState(() {
            selectedSpots.clear();
         });
    }
    

    Don't forget to choose and set one between 1-3 condition

    I found that you can show the tooltip if inside scatterTouchData:

    1. enable:false and handleBuiltInTouches: false
    2. enable:false and handleBuiltInTouches: true
    3. enable:true and handleBuiltInTouches: false

    but not for enable:true and handleBuiltInTouches: true Who knows why this happened? I'll updated if I found the answer or you can comment to complete the answer.

    Full code

    class ScatterChartSample2 extends StatefulWidget {
      const ScatterChartSample2({Key? key}) : super(key: key);
    
      @override
      State<StatefulWidget> createState() => _ScatterChartSample2State();
    }
    
    class _ScatterChartSample2State extends State {
      int touchedIndex = -1;
    
      Color greyColor = Colors.grey;
    
      List<int> selectedSpots = [];
    
      @override
      Widget build(BuildContext context) {
        return AspectRatio(
          aspectRatio: 1,
          child: Card(
            color: const Color(0xff222222),
            child: fl.ScatterChart(
              fl.ScatterChartData(
                scatterSpots: [
                  fl.ScatterSpot(
                    4,
                    4,
                    color: selectedSpots.contains(0) ? Colors.green : greyColor,
                  ),
                  fl.ScatterSpot(
                    2,
                    5,
                    color: selectedSpots.contains(1) ? Colors.yellow : greyColor,
                    radius: 12,
                  ),
                  fl.ScatterSpot(
                    4,
                    5,
                    color:
                    selectedSpots.contains(2) ? Colors.purpleAccent : greyColor,
                    radius: 8,
                  ),
                  fl.ScatterSpot(
                    8,
                    6,
                    color: selectedSpots.contains(3) ? Colors.orange : greyColor,
                    radius: 20,
                  ),
                  fl.ScatterSpot(
                    5,
                    7,
                    color: selectedSpots.contains(4) ? Colors.brown : greyColor,
                    radius: 14,
                  ),
                  fl.ScatterSpot(
                    7,
                    2,
                    color: selectedSpots.contains(5)
                        ? Colors.lightGreenAccent
                        : greyColor,
                    radius: 18,
                  ),
                  fl.ScatterSpot(
                    3,
                    2,
                    color: selectedSpots.contains(6) ? Colors.red : greyColor,
                    radius: 36,
                  ),
                  fl.ScatterSpot(
                    2,
                    8,
                    color:
                    selectedSpots.contains(7) ? Colors.tealAccent : greyColor,
                    radius: 22,
                  ),
                ],
                minX: 0,
                maxX: 10,
                minY: 0,
                maxY: 10,
                borderData: fl.FlBorderData(
                  show: false,
                ),
                gridData: fl.FlGridData(
                  show: true,
                  drawHorizontalLine: true,
                  checkToShowHorizontalLine: (value) => true,
                  getDrawingHorizontalLine: (value) =>
                      fl.FlLine(color: Colors.white.withOpacity(0.1)),
                  drawVerticalLine: true,
                  checkToShowVerticalLine: (value) => true,
                  getDrawingVerticalLine: (value) =>
                      fl.FlLine(color: Colors.white.withOpacity(0.1)),
                ),
                titlesData: fl.FlTitlesData(
                  show: false,
                ),
                showingTooltipIndicators: selectedSpots,
                scatterTouchData: fl.ScatterTouchData(
    // ====================== Set this false =================================
                  enabled: false,
                  handleBuiltInTouches: true,
                  touchTooltipData: fl.ScatterTouchTooltipData(
                    tooltipBgColor: Colors.black,
                    getTooltipItems: (fl.ScatterSpot touchedBarSpot) {
                      return fl.ScatterTooltipItem(
                        'X: ',
                        textStyle: TextStyle(
                          height: 1.2,
                          color: Colors.grey[100],
                          fontStyle: FontStyle.italic,
                        ),
                        bottomMargin: 10,
                        children: [
                          TextSpan(
                            text: '${touchedBarSpot.x.toInt()} \n',
                            style: const TextStyle(
                              color: Colors.white,
                              fontStyle: FontStyle.normal,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                          TextSpan(
                            text: 'Y: ',
                            style: TextStyle(
                              height: 1.2,
                              color: Colors.grey[100],
                              fontStyle: FontStyle.italic,
                            ),
                          ),
                          TextSpan(
                            text: touchedBarSpot.y.toInt().toString(),
                            style: const TextStyle(
                              color: Colors.white,
                              fontStyle: FontStyle.normal,
                              fontWeight: FontWeight.bold,
                            ),
                          ),
                        ],
                      );
                    },
                  ),
                  touchCallback: (fl.FlTouchEvent event,
                      fl.ScatterTouchResponse? touchResponse) {
                    if (touchResponse == null ||
                        touchResponse.touchedSpot == null) {
                      return;
                    }
                    // Show tooltip if tap down detected
                   if (event is fl.FlTapDownEvent) {
                     final sectionIndex = touchResponse.touchedSpot!.spotIndex;
                     setState(() {
                       selectedSpots.add(sectionIndex);
                     });
                // Hide/clear tooltip if long press was ended or tap up detected
                  }else if(event is fl.FlLongPressEnd || event is fl.FlTapUpEvent){
                    setState(() {
                      selectedSpots.clear();
                    });
                   }
                  },
                ),
              ),
            ),
          ),
        );
      }
    }