Create Stunning Circular Progress Bars with Flutter Radial Gauge: Part 1
Detailed Blog page Skeleton loader
Create Stunning Circular Progress Bars with Flutter Radial Gauge: Part 1

TL;DR: The blog introduces the Syncfusion Flutter Radial Gauge widget for visualizing data and tracking progress in circular formats. It covers creating animated circular progress indicators in styles like determinate, indeterminate, and segmented. Steps include configuring the Radial Gauge, adding dependencies, initializing the widget, and customizing progress bar styles. Detailed code examples show how to adjust axis labels, ticks, values, axis lines, range pointer styles, and add custom annotations.

The Syncfusion Flutter Radial Gauge widget is a multi-purpose data visualization widget. It visualizes different types of data and displays the progress of various processes in a circular format.

We have already published blogs on different use cases, such as creating a speedometer and temperature monitor using the Flutter Radial Gauge. To continue these use-case series posts, we will create different styles of animated circular progress indicators using the Syncfusion Flutter Radial Gauge.

The circular progress bar is used to visualize the work progress or an operation such as a download, file transfer, or installation. It can be used for showing different progress states, such as:

  • Determinate
  • Indeterminate
  • Segmented progress

I’ve separated designing various styles for circular progress bars into a two-part blog. In this first part, you will learn about building different styles of a determinate-type circular progress bar.

Circular Progress Bar Styles
Circular Progress Bar Styles

Let’s get started!

Configuring the Radial Gauge widget

Creating a Flutter project

First, we need to configure the Radial Gauge widget in our application. To do this, follow the instructions in the Getting Started documentation to create a basic project in Flutter.

Add Radial Gauge dependency

Include the Syncfusion Flutter Radial Gauge package dependency in the pubspec.yaml file of your project.

syncfusion_flutter_gauges: ^18.2.44

Get packages

To download the package to the local disk, run the following command in your project terminal window.

$ flutter pub get

Import package

Import the Radial Gauge package in the main.dart using the following code example.

import 'package:syncfusion_flutter_gauges/gauges.dart';

Add Radial Gauge widget

After importing the Radial Gauge package to the sample, initialize the Radial Gauge widget and add it to the widget tree, as shown in the following code example.

@override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: SfRadialGauge(),
      ),
    );
  }

Now that we have configured the Radial Gauge widget in our application. Let’s see the magical ways to create charming styles in it.

Various styles in determinate-type circular progress bar

A determinate-type progress bar is used when it is possible to estimate the completion percentage of a process. Let’s see how to design the following styles in a determinate progress bar by using the Flutter Radial Gauge:

To achieve all these determinate-type circular progress bar designs, you need to use the radial axis, range pointer, and annotation features in the Radial Gauge.

Normal progress bar style

Normal Progress Bar
Normal Progress Bar

To get the normal progress bar style, we need to disable the labels and ticks properties in the RadialAxis class and set the axis range values in the minimum and maximum properties based on your design needs.

To show 100 percent progress, you need to define the axis’s minimum value as 0 and maximum as 100. You can also show the progress line (track) of the progress bar by customizing the axisLineStyle, as shown in the following code example.

SfRadialGauge(axes: <RadialAxis>[
                      RadialAxis(
                        minimum: 0,
                        maximum: 100,
                        showLabels: false,
                        showTicks: false,
                        axisLineStyle: AxisLineStyle(
                          thickness: 0.2,
                          cornerStyle: CornerStyle.bothCurve,
                          color: Color.fromARGB(30, 0, 169, 181),
                          thicknessUnit: GaugeSizeUnit.factor,
                        ),
                      )
                    ]),
Progress Bar with Progress Line (Track)
Progress Bar with Progress Line (Track)

You can add a pointer to the progress bar by customizing the position and size of the RangePointer class. To show progress, the pointer Value should be updated with a delay action, such as a timer. Then, the pointer value will be dynamically updated by a timer for a specific duration of time. Refer to the following code example.

pointers: <GaugePointer>[
  RangePointer(
  value: progressValue,
  cornerStyle: CornerStyle.bothCurve,
  width: 0.2,
  sizeUnit: GaugeSizeUnit.factor,
   )
  ],
Progress Bar with Custom Range Pointer
Progress Bar with Custom Range Pointer

To add custom content to the center of the circular progress bar to indicate the completion of a progression or to convey its current status, you can use the annotations feature. To display the current updated progress value, set the pointer value to the annotation text.

annotations: <GaugeAnnotation>[
  GaugeAnnotation(
  positionFactor: 0.1,
  angle: 90,
  widget: Text(
  progressValue.toStringAsFixed(0) + ' / 100',
  style: TextStyle(fontSize: 11),
  ))
  ])
Progress Bar with Annotation
Progress Bar with Annotation

Filled-track and filled-style progress bar

To fill the track color, set the axis line thickness to 1, thicknessUnit to GaugeSizeUnit.factor, and the axis line color to fill the entire gauge radius. Add the range pointer with the offset position to show the progression.

RadialAxis(
                        minimum: 0,
                        maximum: 100,
                        showLabels: false,
                        showTicks: false,
                        startAngle: 270,
                        endAngle: 270,
                        axisLineStyle: AxisLineStyle(
                          thickness: 1,
                          color: const Color.fromARGB(255, 0, 169, 181),
                          thicknessUnit: GaugeSizeUnit.factor,
                        ),
                        pointers: <GaugePointer>[
                          RangePointer(
                            value: progressValue,
                            width: 0.15
                            color: Colors.white,
                            pointerOffset: 0.1,
                            cornerStyle: CornerStyle.bothCurve,
                            sizeUnit: GaugeSizeUnit.factor,
                          )
                        ],

Add an annotation with the current progress value, as explained in the example of the previous code.

Filled-Track-Style Progress Bar
Filled-Track-Style Progress Bar

To fill the progress color, set the range pointer width to 0.95, sizeUnit to GaugeSizeUnit.factor, and the pointer’s color to fill the entire gauge radius. Add an axis line with appropriate thickness to show the track color.

RadialAxis(
                      minimum: 0,
                      maximum: 100,
                      showLabels: false,
                      showTicks: false,
                      startAngle: 270,
                      endAngle: 270,
                      axisLineStyle: AxisLineStyle(
                        thickness: 0.05,
                        color: const Color.fromARGB(100, 0, 169, 181),
                        thicknessUnit: GaugeSizeUnit.factor,
                      ),
                      pointers: <GaugePointer>[
                        RangePointer(
                          value: progressValue,
                          width: 0.95,
                          pointerOffset: 0.05,
                          sizeUnit: GaugeSizeUnit.factor,
                        )
                      ],
                    )
Filled Progress Style
Filled Progress Style

Gradient progress bar with marker style

To apply a gradient to the progress bar, set the SweepGradient with its appropriate colors and offset values to the gradient property of the range pointer. Also, add the MarkerPointer and the range pointer and use the same progress value to update both pointers, as shown in the following code example.

pointers: <GaugePointer>[
                        RangePointer(
                            value: progressValue,
                            width: 0.1,
                            sizeUnit: GaugeSizeUnit.factor,
                            cornerStyle: CornerStyle.startCurve,
                            gradient: const SweepGradient(colors: <Color>[
                              Color(0xFF00a9b5),
                              Color(0xFFa4edeb)
                            ], stops: <double>[
                              0.25,
                              0.75
                            ])),
                        MarkerPointer(
                          value: progressValue,
                          markerType: MarkerType.circle,
                          color: const Color(0xFF87e8e8),
                        )
                      ],
Gradient Progress with Marker Style
Gradient Progress with Marker Style

Semi-circular progress bar style

You can customize the startAngle and endAngle properties of the radial axis to design full and semi-circular progress bars. To make a semi-circle progress bar, set the startAngle value to 180 and the endAngle value to 0, as shown in the following code example.

RadialAxis(
                      showLabels: false,
                      showTicks: false,
                      startAngle: 180,
                      endAngle: 0,
                      radiusFactor: 0.7,
                      canScaleToFit: true,
                      axisLineStyle: AxisLineStyle(
                        thickness: 0.1,
                        color: const Color.fromARGB(30, 0, 169, 181),
                        thicknessUnit: GaugeSizeUnit.factor,
                        cornerStyle: CornerStyle.startCurve,
                      ),
                      pointers: <GaugePointer>[
                        RangePointer(
                            value: progressValue,
                            width: 0.1,
                            sizeUnit: GaugeSizeUnit.factor,
                            cornerStyle: CornerStyle.bothCurve)
                      ],
Semi-Circular Progress Bar Style
Semi-Circular Progress Bar Style

Buffer-style progress bar

In this buffer-style progress bar, you can use a secondary progress indicator to denote the primary progression, which depends on the secondary progression. This style will allow you to visualize both primary and secondary progressions simultaneously. To add primary and secondary progress pointers, use two range pointers with different progress values.

pointers: <GaugePointer>[
                      RangePointer(
                          value: secondaryProgressValue,
                          width: 0.1,
                          sizeUnit: GaugeSizeUnit.factor,
                          color: const Color.fromARGB(120, 0, 169, 181),
                          cornerStyle: CornerStyle.bothCurve),
                      RangePointer(
                          value: progressValue,
                          width: 0.1,
                          sizeUnit: GaugeSizeUnit.factor,
                          cornerStyle: CornerStyle.bothCurve)
                    ],
Buffer Progress Bar
Buffer Progress Bar

Segmented circular progress bar style

The segmented circular progress bar style allows you to divide a progress bar into multiple segments to visualize the progress of multi-sequence tasks.

Segmented Circular Progress Bar Styles
Segmented Circular Progress Bar Styles

Design the segmented progress bar by customizing the RadialAxis and the RangePointer. In addition, you need to add one more RadialAxis over the first axis to create the segmented line in the progress bar. The segmented lines are generated by enabling the major ticks for the secondary radial axis with a certain interval and disabling the other axis elements.

axes: <RadialAxis>[
                    // Create a primary radial axis
                    RadialAxis(
                      minimum: 0,
                      maximum: 100,
                      showLabels: false,
                      showTicks: false,
                      startAngle: 270,
                      endAngle: 270,
                      radiusFactor: 0.7,
                      axisLineStyle: AxisLineStyle(
                        thickness: 0.2,
                        color: const Color.fromARGB(30, 0, 169, 181),
                        thicknessUnit: GaugeSizeUnit.factor,
                      ),
                      pointers: <GaugePointer>[
                        RangePointer(
                            value: progressValue,
                            width: 0.05,
                            pointerOffset: 0.07,
                            sizeUnit: GaugeSizeUnit.factor,
                            )
                      ],
                    ),
                    // Create a secondary radial axis for segmented line
                    RadialAxis(
                      minimum: 0,
                      interval: 1,
                      maximum: 4,
                      showLabels: false,
                      showTicks: true,
                      showAxisLine: false,
                      tickOffset: -0.05,
                      offsetUnit: GaugeSizeUnit.factor,
                      minorTicksPerInterval: 0,
                      startAngle: 270,
                      endAngle: 270,
                      radiusFactor: 0.7,
                      majorTickStyle: MajorTickStyle(
                          length: 0.3,
                          thickness: 3,
                          lengthUnit: GaugeSizeUnit.factor,
                          color: Colors.white),
                    )
                  ]
Segmented Progress Bar
Segmented Progress Bar

Animate progression with real-time data

We have explored different styles of the circular progress bar. Now, let’s see how to update real-time data.

In the real-time application, the progress value will be fetched from a service and updated in the pointer. This demo uses a timer to stimulate progress updates that last 100 milliseconds. The app state will be changed to rebuild the widgets.

The progressValue variable sets the pointer value and the annotation’s text value. In each timer tick, the progressValue variable is incremented by 1 in the setState callback, as shown in the following code. This is to update the pointer and annotation text values.

_timer = Timer.periodic(const Duration(milliseconds: 100),(_timer)
{
  setState(() {
    _progressValue++;
  });
});

In the RangePoiner, set the animationType property to linear and the timer duration to the pointer’s animationDuration property to make the animated progression.

pointers: <GaugePointer>[
                        RangePointer(
                            value: _value1,
                            width: 0.05,
                            sizeUnit: GaugeSizeUnit.factor,
                            enableAnimation: true,
                            animationDuration: 100,
                            animationType: AnimationType.linear)
                      ],

GitHub reference:

You can download the sample code of all the explained circular progress bar types from this GitHub location.

Conclusion

I hope you have enjoyed reading this blog and have a clear idea about how to create various styles for a determinate-type circular progress bar. In the upcoming part 2 blog, you can expect some more beautiful styles.

The Syncfusion Flutter Radial Gauge widget has been designed with flexible UI customization options to adapt to your app easily. It also includes developer-friendly APIs to increase your productivity.

The complete user guide is here, and you can also check out our other samples in this GitHub location. Additionally, you can download and check out our demo app in  Google Play, the App Store, and our website.

The Radial Gauge is also available for our Xamarin, UWP, WinForms, WPF, Blazor, ASP .NET (Core, MVC, Web Forms), JavaScript, Angular, React, and Vue platforms.

Check them out and create stunning circular progress bars!

The newest version of Essential Studio is available on the license and downloads page for existing Syncfusion customers. If you are not a customer, try our 30-day free trial to test the latest features.

You can always contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!

googleplay.png

Be the first to get updates

Sheik Syed

Meet the Author

Sheik Syed

Sheik Syed Abthaheer is a Product Manager at Syncfusion. He has been a .NET developer since 2012, working on the custom control development for Microsoft technologies.

Comments (6)

many thanks for such useful article, but how to control widget size, i follow the tutorial but the widget is very large and is occupying the whole screen

@ shimaa  

Hi SHIMAA,

You can apply the size of the radial gauge widget by wrapping the widget inside the Container or SizedBox widgets and setting the appropriate size as shown in the code below.

Container(
height: 120,
width: 120,
child: SfRadialGauge()
)

Please refer to the below example code.
https://github.com/SyncfusionExamples/Flutter_circular_progress_bar/blob/master/circular_progress_bar/lib/main.dart#L81

Hello Sheik,
Thanks for such a great work.
I would like to ask for a little assistance. I tried using the circular_progress_bar inside an alert dialog but when I do, the progress doesn’t work, I don’t know what I wasn’t doing right.
Below is my code:

import ‘dart:async’;
import ‘package:flutter/material.dart’;
import ‘package:syncfusion_flutter_gauges/gauges.dart’;

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ‘Flutter Demo’,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: ‘Flutter Demo Home Page’),
);
}
}

class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);

final String title;

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

class _MyHomePageState extends State {
// int _counter = 0;
double progressValue = 0;
double _size = 150;
Timer _timer;

@override
void initState() {
super.initState();
if (mounted) {
_timer = Timer.periodic(const Duration(milliseconds: 30), (Timer _timer) {
setState(() {
if (progressValue == 100) {
progressValue = 0;
} else {
progressValue++;
}
});
});
}
}

@override
void dispose() {
_timer.cancel();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: RaisedButton(
child: Text(‘Show alert’),
onPressed: () {
_showMyDialog();
},
),
),
);
}

Future _showMyDialog() {
return showDialog(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
backgroundColor: Colors.transparent,
content: Container(
height: _size,
width: _size,
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(10.0)
),
child: SfRadialGauge(axes: [
RadialAxis(
minimum: 0,
maximum: 100,
showLabels: false,
showTicks: false,
startAngle: 120,
endAngle: 120,
radiusFactor: 0.8,
axisLineStyle: AxisLineStyle(
thickness: 1,
color: Colors.transparent,
thicknessUnit: GaugeSizeUnit.factor,
),
pointers: [
RangePointer(
value: progressValue,
width: 0.1,
enableAnimation: true,
animationDuration: 20,
color: Colors.deepOrangeAccent,
pointerOffset: 0.1,
cornerStyle: CornerStyle.bothCurve,
animationType: AnimationType.linear,
sizeUnit: GaugeSizeUnit.factor,
)
],
annotations: [
GaugeAnnotation(
positionFactor: 0.5,
widget: Icon(
Icons.ac_unit,
color: Colors.deepOrangeAccent,
size: 34.0,
),
),
]
)
]
),
),
);
},
);
}
}

Jayavigneshwaran G
Jayavigneshwaran G
@ David  

Hi DAVID,

The reported problem occurs due to the showDialog widget of Flutter. The progress value incremented in the periodic timer is not bound to the range pointer value once the radial gauge is loaded using showDialog. We have checked by binding the progress value as the text of the Text widget and in that widget also, the incremented progress value is not getting updated. As this is a flutter framework (showDialog) issue, we will log this in GitHub.

As of now, we have modified the provided code by returning the radial gauge as the child of alert dialog from a separate stateful widget. Please find the modified sample from the below link
https://www.syncfusion.com/downloads/support/directtrac/318568/ze/progess_bar_2-346038809

where is the second part?

Gayathri Ramalingam
Gayathri Ramalingam
@ Emiliano  

Hi EMILIANO,

Please find the Part-2 link from below,
https://www.syncfusion.com/blogs/post/create-stunning-circular-progress-bars-with-flutter-radial-gauge-part-2.aspx

Regards,
Gayathri R

Comments are closed.