Listen
Copied RSS Feed

Flutter

Spice Up Your Data Visualization with Flutter Charts Series Customization

TL;DR: Explore how to customize the series in Syncfusion Flutter Charts. Learn to modify series colors, apply gradients, and use custom renderers. Discover detailed implementation steps and code examples. Enhance your data visualization with these powerful customization options. 

Effective communication often hinges on the clarity of data presentation, and utilizing charts can significantly enhance the conveyance of information, making it both clear and compelling. Syncfusion Flutter Charts widget has extensive customization features, allowing you to fine-tune your charts to meet your unique requirements.

In this blog, we’ll explore the options available to customize the series’ appearance in the Syncfusion Flutter Charts.

Let’s get started!

Series customization options

Let’s see the built-in options to customize the visual representation of the series in the Flutter Charts widget:

  • Color and border-color
  • Point color mapper
  • Gradient
  • Border gradient
  • Shader
  • Customize segment method
  • Custom series renderer

The following table displays the customization options supported by the Syncfusion Flutter Cartesian Charts series.

Cartesian series

Point color mapper

Gradient

Border gradient

Shader

Area series

No

Yes

Yes

Yes

Bar series

Yes

Yes

Yes

Yes

Box and Whiskers series

Yes

Yes

Yes

Yes

Bubble series

Yes

Yes

Yes

Yes

Candle series

Yes

No

No

Yes

Column series

Yes

Yes

Yes

Yes

Error bar series

Yes

No

No

Yes

Fastline series

No

Yes

No

Yes

Hilo open close series

Yes

No

No

Yes

Hilo series

Yes

No

No

Yes

Histogram series

Yes

Yes

Yes

Yes

Line series

Yes

No

No

Yes

Range area series

No

Yes

Yes

Yes

Range column series

Yes

Yes

Yes

Yes

Scatter series

Yes

Yes

Yes

Yes

Spline series

Yes

No

No

Yes

Spline area series

No

Yes

Yes

Yes

Spline range area series

No

Yes

Yes

Yes

Stacked area series

No

Yes

Yes

Yes

Stacked area 100% series

No

Yes

Yes

Yes

Stacked bar series

Yes

Yes

Yes

Yes

Stacked bar 100% series

Yes

Yes

Yes

Yes

Stacked column series

Yes

Yes

Yes

Yes

Stacked column 100% series

Yes

Yes

Yes

Yes

Stacked line series

Yes

No

No

Yes

Stacked line 100% series

Yes

No

No

Yes

Step area series

No

Yes

Yes

Yes

Step line series

Yes

No

No

Yes

Waterfall series

Yes

Yes

Yes

Yes

Refer to the following table to know the customization options supported by the other Flutter Charts series.

Other series

Point color mapper

Gradient

Border gradient

Point shader

Doughnut series

Yes

No

No

Yes

Funnel series

Yes

No

No

No

Pie series

Yes

No

No

Yes

Pyramid series

Yes

No

No

No

Radial Bar series

Yes

No

No

Yes

Let’s explore these customization options with code examples!

Color and border-color

The color and border color properties accept a solid color and will be applied to the entire series. Since the fill color and border color properties are unavailable across all series types, these customization options are limited to specific series. For instance, the bar and area series’ allow customization of fill and border colors, whereas the line series supports color and width customizations.

Additionally, the area type series supports a border drawing mode option, specifying which sides of the border should be rendered. This can include all sides, only the top of the area series, or excluding the bottom portion.

Refer to the following code example.

SfCartesianChart(
  series: <CartesianSeries<ChartData, num>>[
    LineSeries(
      ...
      color: Colors.green,
      width: 2,
      ...
    ),
    BarSeries(
      ...
      color: Colors.indigo.withOpacity(0.2),
      borderColor: Colors.indigo,
      width: 2,
      ...
    ),
    AreSeries(
      ...
      color: Colors.purple.withOpacity(0.2),
      borderColor: Colors.purple,
      width: 2,
      borderDrawMode: BorderDrawMode.top,
      ...
    ),
    ...
  ],
)

Refer to the following image.

Color and border color customization options in the Flutter Charts series

Point color mapper

The point color mapper is supported in all Flutter Cartesian Chart series types except the area-type series. It allows us to customize the color of individual series segments. The pointColorMapper callback is called whenever there is a need for the segment color, such as adding or updating the segment, when the data source changes, and more.

In the following code example, the point color is returned from the Colors.accents list using the chart series data point index.

SfCartesianChart(
  series: <CartesianSeries<ChartData, num>>[
    ColumnSeries(
      ...
      pointColorMapper: (ChartData data, int index) {
        if (index == 0) {
          return Colors.accents[0];
        } else {
          return Colors.accents[Colors.accents.length % index];
        }
      },
    ),
  ],
)

Refer to the following image.

Point color mapper in the Flutter Charts series

Gradient

It is a type of LinearGradient specifically designed to be applied across individual segments within a given series.

For example, if we have a gradient transitioning from blue to red and a ColumnSeries with two data points of 20 and 100, the gradient will be applied uniformly to segments regardless of their values. Ideally, if we were to identify a y-axis midpoint value such as 50, we would expect to see a red tint from the midpoint to above, and the midpoint to below would only have a blue shade. However, the gradient will be applied entirely throughout the respected segment, so both the 20 and 50 data points will have red and blue shades.

In the following code example, a vertical gradient is used with red and blue colors.

ColumnSeries(
  ...
  gradient: LinearGradient(
    colors: <Color>[Colors.red, Colors.blue],
    begin: Alignment.topCenter,
    end: Alignment.bottomCenter,
    stops: const <double>[0, 1],
  ),
)

Refer to the following image.

Gradient support in the Flutter Charts series

Border gradient

Like the chart series gradient, a border gradient is a type of LinearGradient specifically designed to be applied across individual segments’ borders within a given series. It acts the same as a series gradient, meaning the gradient will be applied uniformly to segments regardless of their values.

Refer to the following code example.

ColumnSeries(
  ...
  borderWidth: 2,
  borderRadius: BorderRadius.circular(10),
  borderGradient: LinearGradient(
    colors: <Color>[Colors.red, Colors.blue],
    begin: Alignment.topCenter,
    end: Alignment.bottomCenter,
    stops: const <double>[0, 1],
  ),
)

Refer to the following image.

Border gradient support in the Flutter Charts series

Shader

The shader will be applied to the whole series, not specific segments. The onCreateShader is a callback that is called whenever there is a need for the segment or series color, such as adding or updating, when the data source changes, etc., similar to the pointColorMapper.

For example, if we have a vertical gradient transitioning from blue to red and a ColumnSeries with two data points of 20 and 100, the gradient will be applied uniformly to the entire series. Ideally, if we were to identify a y-axis midpoint value such as 50, the red tint would start from the midpoint and extend above, while only having a blue shade below the midpoint. So, the segment with a value of 20 would only have a blue tint, but the segment with a value of 100 should have a gradient from blue to red.

The shader callback brings the following parameters:

  • rect – Holds the respected series render box bounds.
  • renderType – Denotes whether this is called for series or legend.

Refer to the following code example.

ColumnSeries(
  ...
  borderRadius: BorderRadius.circular(10),
  onCreateShader: (ShaderDetails details) {
    return LinearGradient(
      colors: <Color>[Colors.red, Colors.blue],
      begin: Alignment.topCenter,
      end: Alignment.bottomCenter,
      stops: const <double>[0, 1],
    ).createShader(details.rect);
  },
)

Refer to the following image.

Shader feature in the Flutter Charts series

Range-based shader customization

Since the shader returned from onCreateShader will be applied consistently throughout the series, it is beneficial for anticipating color customization based on positive and negative values.

For example, the goal is to visually represent positive values with varying shades of green and negative values with different shades of red.

To accurately apply these color distinctions, it is essential to determine the exact position where the y-axis crosses zero, which serves as the demarcation line between positive and negative values.

Locating the zero position on the y-axis can be accomplished using a clever technique that utilizes the onActualRangeChanged callback function of the axis. This function is triggered when there is a change in the actual range being displayed on the chart. Within this callback, you can calculate the position of the zero value on the y-axis by conducting a simple mathematical operation.

Take the maximum visible value on the y-axis, referred to as visibleMax, and divide it by the total range of values currently visible on the chart. The result of this division will give you a ratio (a position factor) corresponding to the location of the zero value of the y-axis.

Use this position factor as a midpoint color stop for a LinearGradient to create variations in shades of green and red, as illustrated in the following code example.

To ensure the shader is effectively applied as a mask on the entire series rendering, make sure the series’ color and borderColor properties are set. This allows the shader to mask the color correctly. If the borderColor is not set, the shader will not be applied to the border.

double _originColorStopY = 0;

SfCartesianChart(
  primaryXAxis: const DateTimeAxis(),
  onActualRangeChanged: (ActualRangeChangedArgs args) {
    if (args.orientation == AxisOrientation.vertical) {
      final num range = args.visibleMax.abs() + args.visibleMin.abs();
      _originColorStopY = args.visibleMax / range;
    }
  },
  ...
)

SplineAreaSeries(
  ...
  color: Colors.green,
  borderColor: Colors.green,
  onCreateShader: (ShaderDetails details) {
    return ui.Gradient.linear(
      details.rect.topCenter,
      details.rect.bottomCenter,
      <Color>[
        Colors.green,
        Colors.green,
        Colors.red,
        Colors.red,
      ],
      <double>[0, _originColorStopY, _originColorStopY, 1],
    );
  },
)

In this example, we used four colors and their corresponding color stop points. The color stops [0, _originColorStopY, _originColorStopY, 1] will be divided into the following segments:

  1. From 0 to _originColorStopY, the colors are mapped as green to green. As a result, we do not observe any gradient transition; instead, we see a solid green color representing the positive value position.
  2. From _originColorStopY to _originColorStopY, this transition is not visible since the start and end stops are the same.
  3. From _originColorStopY to 1, the colors are mapped as red to red. This results in a solid red color transition, indicating the negative portion.

Note: To add opacity into the shader, it is essential to incorporate the corresponding opacity value into the series’ color property. By doing this, the shader will derive the opacity value from the color property and then apply it accordingly. This is the current behavior of the Flutter framework to add an opacity to the shader.

Refer to the following image.

Range-based shader customization in the Flutter Charts series

Point shader

It is specific to CircularSeries and is used to apply individual gradients specific to a segment.

In the following code example, gradient colors are loaded from a data source and applied using RadialGradient to individual segments.

SfCircularChart(
  series: <CircularSeries<CircularChartData, String>>[
   DoughnutSeries(
     dataSource: <CircularChartData>[
        CircularChartData('2021', 20, Colors.red, Colors.orange),
        CircularChartData('2022', 50, Colors.orange, Colors.purple),
        CircularChartData('2023', 100, Colors.purple, Colors.red),
      ],
      ...
      pointShaderMapper: (dynamic data, int index, Color color, Rect rect) {
        return RadialGradient(
          colors: <Color>[data.start, data.end],
          stops: const <double>[0, 1],
        ).createShader(rect);
      },
      ...
    ),
  ],
)

Refer to the following image.

Point shader customization in the Flutter Charts series

Customize chart series segment through method

The customizeSegment method is designed to tailor the visual aspects of a segment according to its index or specific conditions. This method can be overridden in the subclass of the series renderer. Within the customizeSegment, properties such as fillPaint and strokePaint are accessible, allowing for the segment’s color modifications.

In the following code example, the shader is applied to the segment with the 10th index. We currently do not have direct properties to apply a gradient to individual segments, but this can be achieved using the customizeSegment method.

class _ColumnSeriesRenderer extends ColumnSeriesRenderer<ChartData, num> {
  @override
  void customizeSegment(ChartSegment segment) {
    super.customizeSegment(segment);
    if (segment.currentSegmentIndex == 10) {
      segment.fillPaint.shader = const LinearGradient(
        colors: <Color>[Colors.red, Colors.orange],
        begin: Alignment.topCenter,
        end: Alignment.bottomCenter,
        stops: <double>[0, 1],
      ).createShader(paintBounds);
    } else {
      segment.fillPaint.shader = null;
    }
  }
}

Refer to the following image.

Customizing the Flutter Charts series segments using methods

Custom series renderer

This is the final and most important option available in the series, allowing us to render our own series. Creating our series renderer allows us to draw the required shapes on each segment.

To do this, create a custom renderer class by extending an existing series renderer and then sharing this custom renderer with the series through the onCreateRenderer callback. The following are the major key factors of the renderer:

Then, create a custom segment class by extending an existing segment class, mapping it with the series renderer by overriding the createSegment method, and returning it.

Once the custom segment is mapped with the series, the base implementation passes the respective data point values into the segment and transforms them into pixel values. Therefore, in the next step, override the segment’s onPaint method to implement the desired drawing logic.

In the following code example, we have drawn an oval shape to replace a column segment.

ColumnSeries(
  ...
  borderColor: Colors.pink,
  onCreateRenderer: (ChartSeries<ChartData, num> series) {
    return _ColumnSeriesRenderer();
  },
)

class _ColumnSeriesRenderer extends ColumnSeriesRenderer<ChartData, num> {
  @override
  ColumnSegment<ChartData, num> createSegment() => _ColumnSegment();
}

class _ColumnSegment extends ColumnSegment<ChartData, num> {
  @override
  void onPaint(Canvas canvas) {
    if (segmentRect == null) {
      return;
    }
    final Rect fillRect = segmentRect!.outerRect;
    canvas.drawOval(fillRect, fillPaint);
    final Rect borderRect = fillRect.deflate(series.borderWidth / 2);
    canvas.drawOval(borderRect, strokePaint);
  }
}

Refer to the following image.

Custom series rendering feature in the Flutter Charts

GitHub reference

For more details, refer to the Flutter Charts series customization GitHub demos.

Unlock the power of Syncfusion’s highly customizable Flutter widgets.

Conclusion

Thanks for reading! In this blog, we’ve explored the series customization options in the Syncfusion Flutter Charts widget. Use these features to visualize your data and get valuable insights. Give it a try, and leave your feedback in the comment section below.

If you need a new widget for the Flutter framework or new features in our existing widgets, you can contact us through our support forumsupport portal, or feedback portal. As always, we are happy to assist you!

Related blogs

Meet the Author

Vijayakumar Mariappan

Vijayakumar Mariappan is a Product Manager at Syncfusion who currently manages Flutter products. He's also been a .NET developer since 2015 with expertise in Xamarin.Forms, WPF, and UWP.