Controller in Flutter Range Selector (SfRangeSelector)

18 Nov 201824 minutes to read

You can use RangeController for setting and getting current selected values of Flutter Range Selector.

The start represents the currently selected start value of the Flutter Range Selector. The left thumb of the Range Selector is drawn corresponding to this value.

The end represents the currently selected end value of the Flutter Range Selector. The right thumb of the Range Selector is drawn corresponding to this value.

You can get previous values using previousStart and previousEnd properties.

The start, end, previousStart, previousEnd properties can be either double or DateTime based on whether it is date type SfRangeSelector or numeric SfRangeSelector.

IMPORTANT

You do not need to set the initialValues property when using the controller property in the Flutter Range Selector.

NOTE

You must import the Core to use the range controller in the Flutter Range Selector.

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_core/core.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class ControllerSample extends StatefulWidget {
  @override
  _ControllerSampleState createState() => _ControllerSampleState();
}

class _ControllerSampleState extends State<ControllerSample> {
  final double _min = 2.0;
  final double _max = 10.0;
  SfRangeValues _values = SfRangeValues(4.0, 6.0);
  late RangeController _rangeController;

  @override
  void initState() {
    super.initState();
    _rangeController = RangeController(start: _values.start, end: _values.end);
  }

  @override
  void dispose() {
    _rangeController.dispose();
    super.dispose();
  }

  final List<Data> chartData = <Data>[
    Data(x: 2.0, y: 2.2),
    Data(x: 3.0, y: 3.4),
    Data(x: 4.0, y: 2.8),
    Data(x: 5.0, y: 1.6),
    Data(x: 6.0, y: 2.3),
    Data(x: 7.0, y: 2.5),
    Data(x: 8.0, y: 2.9),
    Data(x: 9.0, y: 3.8),
    Data(x: 10.0, y: 3.7),
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: SfRangeSelector(
            min: _min,
            max: _max,
            interval: 1,
            showTicks: true,
            showLabels: true,
            controller: _rangeController,
            child: Container(
              height: 130,
              child: SfCartesianChart(
                margin: const EdgeInsets.all(0),
                primaryXAxis: NumericAxis(
                  minimum: _min,
                  maximum: _max,
                  isVisible: false,
                ),
                primaryYAxis: NumericAxis(isVisible: false),
                plotAreaBorderWidth: 0,
                series: <SplineAreaSeries<Data, double>>[
                  SplineAreaSeries<Data, double>(
                    color: Color.fromARGB(255, 126, 184, 253),
                    dataSource: chartData,
                    xValueMapper: (Data sales, int index) => sales.x,
                    yValueMapper: (Data sales, int index) => sales.y)
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class Data {
  Data({required this.x, required this.y});
  final double x;
  final double y;
}

Range Selector controller

Selection with SfChart

The Flutter Range Selector provides built-in support for selecting the chart segments based on the selected range in the Range Selector. To achieve this segment selection, you must set the SelectionBehavior.selectionController property in the SfCartesianChart.series property. The selectionController links the chart selection to the controller of the Range Controller.

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_core/core.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class SelectionWithSfChartSample extends StatefulWidget {
  @override
  _SelectionWithSfChartSampleState createState() =>
      _SelectionWithSfChartSampleState();
}

class _SelectionWithSfChartSampleState
    extends State<SelectionWithSfChartSample> {
  SfRangeValues _values =
      SfRangeValues(DateTime(2010, 03, 01), DateTime(2010, 06, 01));
  late RangeController _rangeController;

  final List<Data> chartData = <Data>[
    Data(x: DateTime(2010, 01, 01), y: 2.2),
    Data(x: DateTime(2010, 02, 01), y: 3.4),
    Data(x: DateTime(2010, 03, 01), y: 2.8),
    Data(x: DateTime(2010, 04, 01), y: 1.6),
    Data(x: DateTime(2010, 05, 01), y: 2.3),
    Data(x: DateTime(2010, 06, 01), y: 2.5),
    Data(x: DateTime(2010, 07, 01), y: 2.9),
    Data(x: DateTime(2010, 08, 01), y: 3.8),
    Data(x: DateTime(2010, 09, 01), y: 3.7),
  ];

  @override
  void initState() {
    super.initState();
    _rangeController =
        RangeController(start: _values.start, end: _values.end);
  }

  @override
  void dispose() {
    _rangeController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: SfRangeSelector(
            min: DateTime(2010, 01, 01),
            max: DateTime(2010, 09, 01),
            interval: 2,
            dateIntervalType: DateIntervalType.months,
            dateFormat: DateFormat.yM(),
            showTicks: true,
            showLabels: true,
            controller: _rangeController,
            onChanged: (SfRangeValues values) {
              setState(() {});
            },
            child: SizedBox(
              height: 130,
              child: SfCartesianChart(
                margin: EdgeInsets.zero,
                primaryXAxis: DateTimeAxis(
                  minimum: DateTime(2010, 01, 01),
                  maximum: DateTime(2010, 09, 01),
                  isVisible: false,
                ),
                primaryYAxis: const NumericAxis(isVisible: false),
                plotAreaBorderWidth: 0,
                plotAreaBackgroundColor: Colors.transparent,
                series: <ColumnSeries<Data, DateTime>>[
                  ColumnSeries<Data, DateTime>(
                    dataSource: chartData,
                    selectionBehavior: SelectionBehavior(
                      selectionController: _rangeController,
                    ),
                    xValueMapper: (Data sales, int index) => sales.x,
                    yValueMapper: (Data sales, int index) => sales.y,
                    pointColorMapper: (Data sales, int index) {
                      final DateTime start =
                          _rangeController.start as DateTime;
                      final DateTime end = _rangeController.end as DateTime;
                      if (sales.x.isAfter(start) && sales.x.isBefore(end)) {
                        return const Color.fromRGBO(0, 178, 206, 1);
                      }
                      return Colors.transparent;
                    },
                    color: const Color.fromRGBO(255, 255, 255, 0),
                    dashArray: const <double>[5, 4],
                    borderColor: const Color.fromRGBO(194, 194, 194, 1),
                    animationDuration: 0,
                    borderWidth: 1,
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class Data {
  Data({required this.x, required this.y});
  final DateTime x;
  final double y;
}

Selection support

Zooming with SfChart

The Flutter Range Selector provides built-in support for updating the visible range of the chart based on the selected range in the Range Selector. To update the visible range, you must set the primaryXAxis.rangeController property in the SfCartesianChart.

import 'package:flutter/material.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_core/core.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class ZoomingWithSfChartSample extends StatefulWidget {
  @override
  _ZoomingWithSfChartSampleState createState() =>
      _ZoomingWithSfChartSampleState();
}

class _ZoomingWithSfChartSampleState extends State<ZoomingWithSfChartSample> {
  final double _min = 2.0;
  final double _max = 19.0;
  SfRangeValues _values = SfRangeValues(8.0, 16.0);
  late RangeController _rangeController;
  late SfCartesianChart splineChart;

  final List<Data> chartData = <Data>[
    Data(x: 2.0, y: 2.2),
    Data(x: 3.0, y: 3.4),
    Data(x: 4.0, y: 2.8),
    Data(x: 5.0, y: 1.6),
    Data(x: 6.0, y: 2.3),
    Data(x: 7.0, y: 2.5),
    Data(x: 8.0, y: 2.9),
    Data(x: 9.0, y: 3.8),
    Data(x: 10.0, y: 3.7),
    Data(x: 11.0, y: 2.2),
    Data(x: 12.0, y: 3.4),
    Data(x: 13.0, y: 2.8),
    Data(x: 14.0, y: 1.6),
    Data(x: 15.0, y: 2.3),
    Data(x: 16.0, y: 2.5),
    Data(x: 17.0, y: 2.9),
    Data(x: 18.0, y: 3.8),
    Data(x: 19.0, y: 3.7),
  ];

  @override
  void initState() {
    super.initState();
    _rangeController =
        RangeController(start: _values.start, end: _values.end);
  }

  @override
  void dispose() {
    _rangeController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    splineChart = SfCartesianChart(
      margin: const EdgeInsets.only(left: 10, right: 10, bottom: 20),
      primaryXAxis: NumericAxis(
          minimum: _min,
          maximum: _max,
          isVisible: true,
          rangeController: _rangeController),
      primaryYAxis: NumericAxis(isVisible: true),
      plotAreaBorderWidth: 0,
      series: <SplineSeries<Data, double>>[
        SplineSeries<Data, double>(
            color: Color.fromARGB(255, 126, 184, 253),
            dataSource: chartData,
            animationDuration: 0,
            xValueMapper: (Data sales, int index) => sales.x,
            yValueMapper: (Data sales, int index) => sales.y)
      ],
    );

    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Padding(
            padding: EdgeInsets.only(left: 10, right: 10, top: 80),
            child: Column(
              children: <Widget>[
                Container(
                  child: splineChart,
                ),
                SfRangeSelector(
                  min: _min,
                  max: _max,
                  interval: 2,
                  showTicks: true,
                  showLabels: true,
                  controller: _rangeController,
                  child: Container(
                    height: 130,
                    child: SfCartesianChart(
                      margin: const EdgeInsets.all(0),
                      primaryXAxis: NumericAxis(
                          minimum: _min,
                          maximum: _max,
                          isVisible: false),
                      primaryYAxis: NumericAxis(isVisible: false),
                      plotAreaBorderWidth: 0,
                      series: <SplineSeries<Data, double>>[
                        SplineSeries<Data, double>(
                            color: Color.fromARGB(255, 126, 184, 253),
                            dataSource: chartData,
                            xValueMapper: (Data sales, int index) => sales.x,
                            yValueMapper: (Data sales, int index) => sales.y)
                      ],
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class Data {
  Data({required this.x, required this.y});
  final double x;
  final double y;
}

Zooming support

Deferred update

You can control when the dependent components are updated while thumbs are being dragged continuously. It can be achieved by setting the SfRangeSelector.enableDeferredUpdate property and the delay in the update can be achieved by setting the SfRangeSelector.deferredUpdateDelay property. The default value of the deferredUpdateDelay property is 500 milliseconds.

It updates the controller start and end values and invoke the onChanged callback when the thumb is dragged and held for the duration specified in the deferredUpdateDelay. However, range values are immediately updated in touch up action.

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import 'package:syncfusion_flutter_charts/charts.dart';
import 'package:syncfusion_flutter_core/core.dart';
import 'package:syncfusion_flutter_sliders/sliders.dart';

class DeferredUpdateSample extends StatefulWidget {
  @override
  _DeferredUpdateSampleState createState() => _DeferredUpdateSampleState();
}

class _DeferredUpdateSampleState extends State<DeferredUpdateSample> {
  final DateTime min = DateTime(2000, 01, 01, 0), max = DateTime(2000, 12, 15);
  late RangeController rangeController;
  late SfCartesianChart splineAreaChart;
  late SfCartesianChart splineChart;
  late List<Data> data;

  @override
  void initState() {
    super.initState();
    rangeController = RangeController(
      start: DateTime(2000, 04, 15),
      end: DateTime(2000, 07, 15),
    );

    data = <Data>[
      Data(x: DateTime(2000, 01, 01, 0), y: 100),
      Data(x: DateTime(2000, 01, 15), y: 10),
      Data(x: DateTime(2000, 02, 01), y: 40),
      Data(x: DateTime(2000, 02, 15), y: 34),
      Data(x: DateTime(2000, 03, 01), y: 80),
      Data(x: DateTime(2000, 03, 15), y: 49),
      Data(x: DateTime(2000, 04, 01), y: 56),
      Data(x: DateTime(2000, 04, 15), y: 26),
      Data(x: DateTime(2000, 05, 01), y: 8),
      Data(x: DateTime(2000, 05, 15), y: 80),
      Data(x: DateTime(2000, 06, 01), y: 42),
      Data(x: DateTime(2000, 06, 15), y: 12),
      Data(x: DateTime(2000, 07, 01), y: 28),
      Data(x: DateTime(2000, 07, 15), y: 68),
      Data(x: DateTime(2000, 08, 01), y: 94),
      Data(x: DateTime(2000, 08, 15), y: 24),
      Data(x: DateTime(2000, 09, 01), y: 72),
      Data(x: DateTime(2000, 09, 15), y: 32),
      Data(x: DateTime(2000, 10, 01), y: 48),
      Data(x: DateTime(2000, 10, 15), y: 4),
      Data(x: DateTime(2000, 11, 01), y: 64),
      Data(x: DateTime(2000, 11, 15), y: 10),
      Data(x: DateTime(2000, 12, 01), y: 85),
      Data(x: DateTime(2000, 12, 15), y: 96),
    ];

    splineAreaChart = SfCartesianChart(
      margin: const EdgeInsets.all(0),
      primaryXAxis: DateTimeAxis(isVisible: false, maximum: max),
      primaryYAxis: NumericAxis(isVisible: false),
      plotAreaBorderWidth: 0,
      series: <SplineAreaSeries<Data, DateTime>>[
        SplineAreaSeries<Data, DateTime>(
          dataSource: data,
          xValueMapper: (Data sales, int index) => sales.x,
          yValueMapper: (Data sales, int index) => sales.y,
        )
      ],
    );
  }

  @override
  void dispose() {
    rangeController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    splineChart = SfCartesianChart(
      plotAreaBorderWidth: 0,
      primaryXAxis: DateTimeAxis(
          isVisible: false,
          minimum: min,
          maximum: max,
          rangeController: rangeController),
      primaryYAxis: NumericAxis(
        labelPosition: ChartDataLabelPosition.inside,
        labelAlignment: LabelAlignment.end,
        majorTickLines: MajorTickLines(size: 0),
        axisLine: AxisLine(color: Colors.transparent),
      ),
      series: <SplineSeries<Data, DateTime>>[
        SplineSeries<Data, DateTime>(
          dataSource: data,
          animationDuration: 0,
          xValueMapper: (Data sales, int index) => sales.x,
          yValueMapper: (Data sales, int index) => sales.y,
        )
      ],
    );
    final Widget page = Container(
      margin: const EdgeInsets.all(0),
      padding: const EdgeInsets.all(0),
      child: Center(
        child: Column(
          children: <Widget>[
            Expanded(
              child: Container(
                  padding: const EdgeInsets.fromLTRB(5, 20, 15, 25),
                  child: splineChart),
            ),
            Container(
              margin: const EdgeInsets.all(0),
              padding: const EdgeInsets.all(0),
              child: Center(
                child: Padding(
                  padding: const EdgeInsets.fromLTRB(14, 0, 15, 15),
                  child: SfRangeSelector(
                    min: min,
                    max: max,
                    interval: 1,
                    enableDeferredUpdate: true,
                    deferredUpdateDelay: 500,
                    labelPlacement: LabelPlacement.betweenTicks,
                    dateIntervalType: DateIntervalType.months,
                    controller: rangeController,
                    showTicks: true,
                    showLabels: true,
                    dragMode: SliderDragMode.both,
                    labelFormatterCallback:
                        (dynamic actualLabel, String formattedText) {
                      final String label = DateFormat.MMM().format(actualLabel);
                      return label;
                    },
                    onChanged: (SfRangeValues values) {},
                    child: Container(
                      child: splineAreaChart,
                      height: 75,
                      padding: const EdgeInsets.all(0),
                      margin: const EdgeInsets.all(0),
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: Container(height: 400, child: page),
        ),
      ),
    );
  }
}

class Data {
  Data({required this.x, required this.y});
  final DateTime x;
  final double y;
}

Deferred update support