Controller in Flutter Range Selector (SfRangeSelector)

4 May 202124 minutes to read

You can use RangeController for setting and getting current selected values of range selector.

The start represents the currently selected start value of the range selector. The left thumb of the range selector was drawn corresponding to this value.

The end represents the currently selected end value of the range selector. The right thumb of the range selector was 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 need not to set the initialValues property when using controller property in the range selector.

NOTE

You must import the Core to use the range controller in the range selector.

final double _min = 2.0;
final double _max = 10.0;
SfRangeValues _values = SfRangeValues(4.0, 6.0);
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

We have provided built-in support for selecting the chart segments based on the selected range in range selector. To achieve this segment selection, you must set the SelectionSettings.selectionController property in the SfCartesianChart.series property.

SfRangeValues _values = SfRangeValues(DateTime(2010, 03, 01), DateTime(2010, 06, 01));
RangeController _rangeController;
List<int> selectedItems;

@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: 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
Widget build(BuildContext context) {
   selectedItems = <int>[];
   for (int i = 0; i < chartData.length; i++) {
      if (chartData[i].x.millisecondsSinceEpoch >=
          _rangeController.start.millisecondsSinceEpoch &&
          chartData[i].x.millisecondsSinceEpoch <=
              _rangeController.end.millisecondsSinceEpoch) {
        selectedItems.add(chartData.indexOf(chartData[i]));
      }
   }

    return 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,
            child: Container(
              height: 130,
              child: SfCartesianChart(
                margin: const EdgeInsets.all(0),
                primaryXAxis: DateTimeAxis(minimum: DateTime(2010, 01, 01),
                    maximum: DateTime(2010, 09, 01),
                    isVisible: false),
                primaryYAxis: NumericAxis(isVisible: false),
                plotAreaBorderWidth: 0,
                plotAreaBackgroundColor: Colors.transparent,
                series: <ColumnSeries<Data, DateTime>>[
                  ColumnSeries<Data, DateTime>(
                      initialSelectedDataIndexes: selectedItems,
                      selectionSettings: SelectionSettings(
                          enable: true,
                          unselectedOpacity: 0,
                          selectedBorderColor: const Color.fromRGBO(
                              0, 178, 206, 1),
                          selectedColor: const Color.fromRGBO(0, 178, 206, 1),
                          unselectedColor: Colors.transparent,
                          selectionController: _rangeController),
                      color: const Color.fromRGBO(255, 255, 255, 0),
                      dashArray: <double>[5, 4],
                      borderColor: const Color.fromRGBO(194, 194, 194, 1),
                      animationDuration: 0,
                      borderWidth: 1,
                      dataSource: chartData,
                      xValueMapper: (Data sales, int index) => sales.x,
                      yValueMapper: (Data sales, int index) => sales.y)
                ],
              ),
            ),
          ),
        )
    );
}
class Data {
   Data({this.x, this.y});
   final DateTime x;
   final double y;
}

Selection support

Zooming with SfChart

We have provided built-in support for updating the visible range of the chart based on the selected range in range selector. To update the visible range, you must set the primaryYAxis.rangeController property in the SfCartesianChart.

final double _min = 2.0;
final double _max = 19.0;
SfRangeValues _values = SfRangeValues(8.0, 16.0);
RangeController _rangeController;
SfCartesianChart splineChart;

@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),
    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
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 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. 

final DateTime min = DateTime(2000, 01, 01, 0), max = DateTime(2000, 12, 15);
RangeController rangeController;
SfCartesianChart splineAreaChart, splineChart;
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
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) {
                        String label = DateFormat.MMM().format(actualLabel);
                        label = label;
                        return label;
                      },
                      onChanged: (SfRangeValues values) {},
                      child: Container(
                        child: splineAreaChart,
                        height: 75,
                        padding: const EdgeInsets.all(0),
                        margin: const EdgeInsets.all(0),
                      ),
                    ),
                  ),
                ),
              ),
            ],
          ),
        )
     );
    return Scaffold(
      body: Center(
        child: Container(height: 400, child: page),
      )
   );
}

Deferred update support