Вертикальная пунктирная линия на графике

На самом деле я использую две зависимости: диаграмма флаттера syncfusion и пунктирная линия.

Моя диаграмма выглядит так (см. Рисунок 1)

Вы можете нажать на маркер на графике (или на эмодзи баскетбола), чтобы отобразить пунктирную линию и виджет всплывающей подсказки. Моя проблема в том, что если на моем графике недостаточно высоты, отображаемый виджет уходит внизу (что не является нашей целью, см. Рисунок 2). Как я могу добиться, чтобы мой виджет всегда отображался поверх диаграммы?

Я искал переполненные коробки и стопки, но через несколько долгих часов я смог сделать это сам. Код для диаграммы доступен здесь:

https://pastebin.com/6VX1VhRH

       import 'package:dotted_line/dotted_line.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:lifetizr/misc/utils.dart';
import 'package:lifetizr/ui/screens/homepage/homepage_viewmodel.dart';
import 'package:lifetizr/ui/widgets/activity_block_preview.dart';
import 'package:stacked/stacked.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:theme_provider/theme_provider.dart';

class ChartBlock extends StatefulWidget {
  @override
  _ChartBlockState createState() => _ChartBlockState();
}

class _ChartBlockState extends State<ChartBlock> {
  List<_DataPoint> graphData = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  void loadData() async {
    await HomepageViewModel().getGraphData().then(
      (value) {
        List<_DataPoint> tmpData = [];

        tmpData.add(_DataPoint(DateTime.fromMillisecondsSinceEpoch(0).toString(), 290));

        for (var item in value['allBloodglucoses']['edges']) {
          tmpData.add(_DataPoint(
              item['node']['timestamp'], item['node']['bloodGlucose']));
        }

        setState(
          () {
            graphData = tmpData;
          },
        );
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    final controller = ThemeProvider.controllerOf(context);

    return ViewModelBuilder<HomepageViewModel>.reactive(
      viewModelBuilder: () => HomepageViewModel(),
      builder: (context, model, child) {
        return SfCartesianChart(
          trackballBehavior: TrackballBehavior(
              lineWidth: 5,
              lineColor: reverseColorGrabber(controller),
              lineType: TrackballLineType.vertical,
              enable: true,
              shouldAlwaysShow: true,
              tooltipAlignment: ChartAlignment.far,
              tooltipSettings: InteractiveTooltip(
                arrowWidth: 0,
                arrowLength: 0,
                borderColor: reverseColorGrabber(controller),
                borderRadius: 15,
                borderWidth: 15,
                connectorLineWidth: 50,
                format: 'point.y mg/dL',
                enable: true,
                color: reverseColorGrabber(controller),
              )),
          primaryXAxis: CategoryAxis(
              visibleMinimum: 0,
              visibleMaximum: graphData.length.toDouble() / 6),
          zoomPanBehavior: ZoomPanBehavior(
            enablePanning: true,
          ),
          indicators: [],
          // Enable tooltip
          tooltipBehavior: TooltipBehavior(
            color: Colors.transparent,
            canShowMarker: false,
            enable: true,
            builder: (dynamic d, dynamic f, dynamic g, int i, int j) {
              if (i == 1 || i == 4 || i == 3)
                return Container(
                  height: 100,
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.end,
                    children: [
                      Container(
                        padding: EdgeInsets.all(5),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(25),
                          color: Colors.green,
                        ),
                        child: InkWell(
                          onTap: () {
                            print("Pressed");
                          },
                          child: Container(
                              width: 80,
                              child: ActivityBlockPreview(
                                score: 18,
                                pictureUrl: "https://i.imgur.com/nlRr5vn.jpeg",
                              )),
                        ),
                      ),
                      Expanded(
                        child: DottedLine(
                          direction: Axis.vertical,
                          lineLength: double.infinity,
                          lineThickness: 1.0,
                          dashLength: 4.0,
                          dashColor: reverseColorGrabber(controller),
                          dashRadius: 0.0,
                          dashGapLength: 4.0,
                          dashGapColor: Colors.transparent,
                          dashGapRadius: 0.0,
                        ),
                      ),
                    ],
                  ),
                );
              else
                return null;
            },
          ),
          series: <ChartSeries<_DataPoint, String>>[
            SplineSeries<_DataPoint, String>(
              color: reverseColorGrabber(controller),
              width: 5,
              pointColorMapper: (_DataPoint data, int i) {
                if (i == 0) return Colors.transparent;
              },
              dataSource: <_DataPoint>[...graphData],
              xValueMapper: (_DataPoint point, _) =>
                  DateFormat('H:mm').format(DateTime.parse(point.timestamp)),
              yValueMapper: (_DataPoint point, _) => point.glucose,
              // Enable data label
              dataLabelSettings: DataLabelSettings(
                isVisible: true,
                labelAlignment: ChartDataLabelAlignment.top,
                builder: (dynamic d, dynamic f, dynamic g, int i, int _) {
                  if (i == 1 || i == 4)
                    return Container(
                      height: 20,
                      width: 20,
                      decoration: BoxDecoration(
                        borderRadius: BorderRadius.circular(30),
                        border: Border.all(width: 3, color: Colors.red),
                        color: Colors.white,
                      ),
                    );
                  if (i == 3) {
                    return Text(
                      "",
                      style: TextStyle(fontSize: 20),
                    );
                  } else
                    return null;
                },
              ),
            )
          ],
        );
      },
    );
  }
}

class _DataPoint {
  _DataPoint(this.timestamp, this.glucose);

  final String timestamp;
  final double glucose;
}


class ActivityBlockPreview extends StatefulWidget {
  final int score;
  final String pictureUrl;
  final bool isSportActivity;
  final bool isSleepActivity;
  final String sportEmoji;

  const ActivityBlockPreview(
      {this.score,
      this.pictureUrl,
      this.isSportActivity = false,
      this.isSleepActivity = false,
      this.sportEmoji});

  @override
  _ActivityBlockPreviewState createState() => _ActivityBlockPreviewState();
}

class _ActivityBlockPreviewState extends State<ActivityBlockPreview> {
  @override
  Widget build(BuildContext context) {
    var controller = ThemeProvider.controllerOf(context);

    return Container(
      decoration: BoxDecoration(
          color: colorGrabber(controller),
          borderRadius: BorderRadius.circular(60)),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.start,
        children: [
          CircleAvatar(
            backgroundColor: colorGrabber(controller),
            backgroundImage: widget.isSportActivity == false &&
                    widget.isSleepActivity == false
                ? NetworkImage(
                    widget.pictureUrl,
                  )
                : null,
            child: widget.isSportActivity == true || widget.isSleepActivity
                ? Text(widget.sportEmoji)
                : Container(),
          ),
          if (!widget.isSportActivity)
            Padding(
              padding: const EdgeInsets.only(left: 5, right: 10),
              child: Text(
                widget.score.toString(),
                style: TextStyle(
                  fontSize: 20,
                  fontWeight: FontWeight.bold,
                ),
              ),
            )
        ],
      ),
    );
  }
}

1 ответ

Решение

Привет от Syncfusion. Мы проанализировали ваш сценарий с предоставленной информацией на нашей стороне, и в настоящее время, когда над точкой данных нет места для отображения его настраиваемого виджета всплывающей подсказки, он смещается вниз и отображается. Это поведение по умолчанию. Однако, если вы хотите всегда отображать настраиваемый виджет всплывающей подсказки над соответствующей точкой данных, вы можете установить для свойства rangePadding оси диаграммы значение ChartRangePadding.additional, чтобы в начале и в конце оси добавлялись дополнительные отступы диапазона. Кроме того, если значение rangePadding указано в качестве дополнительного не решила проблему, тогда вам нужно установить конкретный диапазон для диаграммы в соответствии с вашими точками данных, доступными с помощью максимального свойства оси диаграммы, чтобы было достаточно места для конструктора виджетов всплывающей подсказки для визуализации над соответствующими данными точка. Дополнительные сведения см. В приведенных ниже фрагментах кода.

Фрагмент кода:

Установка значения свойства rangePadding как дополнительного:

       SfCartesianChart(
          primaryYAxis: NumericAxis(
              rangePadding: ChartRangePadding.additional
          ),
          // Your configurations
)

Установка определенного диапазона для оси Y диаграммы:

       SfCartesianChart(
          primaryYAxis: NumericAxis(
              minimum: 0,
              maximum: 300
          ),
          // Your configurations
)

Пожалуйста, ознакомьтесь с приведенными выше решениями и верните нам, если у вас все еще есть вопросы.

Дополнительные сведения о свойстве rangePadding и настройке диапазона диаграммы см. В руководствах пользователя ниже.

С уважением, Шрирам Киран

Другие вопросы по тегам