Flutter CircularProgressIndicator Tutorial

We’ll examine the Flutter CircularProgressIndicator widget in this tutorial. Any application must have loading indicators to let users know when a process is executing in the background or when the system is busy. The circular progress indicator, which offers a rotating visual clue of progress or loading activities, is one of the most widely used indicators. It also create widget at center circular.

You will learn how to utilise CircularProgressIndicator in this lesson, covering everything from the fundamentals to more complex applications, including customisation and practical examples.

What is CircularProgressIndicator in Flutter?

The CircularProgressIndicator is a material design widget in Flutter that represents a circular, spinning indicator. It is primarily used to show the progress of a task that’s being processed or indicate that the application is busy.

There are two main types of progress indicators:

  1. Indeterminate Progress Indicator: The duration of the task is unknown. This type keeps spinning until the task completes.
  2. Determinate Progress Indicator: The task has a known duration, and the indicator progresses accordingly based on the completion percentage.

In Flutter, CircularProgressIndicator supports both indeterminate and determinate states. Let’s dive into how to implement each of them.

How to Use CircularProgressIndicator?

To use CircularProgressIndicator, simply include it in the widget tree. Here’s a basic example of an indeterminate circular progress indicator:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('CircularProgressIndicator Example'),
        ),
        body: Center(
          child: CircularProgressIndicator(),
        ),
      ),
    );
  }
}

This example displays a basic circular progress indicator that spins indefinitely.

Indeterminate Progress Indicator

An indeterminate progress indicator does not track how much work is done or how much is left. It is ideal for situations where you are unsure of the exact duration or percentage of progress, such as when loading remote data.

In the above example, the circular progress indicator will continue to rotate until the task is completed.

Customizing the Color and Stroke Width

The CircularProgressIndicator can be customized with different properties such as color and strokeWidth. Here’s how you can customize the appearance:

CircularProgressIndicator(
  color: Colors.blue,
  strokeWidth: 6.0,
)
  • color: Sets the color of the progress indicator.
  • strokeWidth: Changes the thickness of the circular indicator. The default value is 4.0.

Example:

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Custom CircularProgressIndicator'),
        ),
        body: Center(
          child: CircularProgressIndicator(
            color: Colors.green,
            strokeWidth: 8.0,
          ),
        ),
      ),
    );
  }
}

In this case, the indicator is green with a thicker stroke width.

Determinate Progress Indicator

A determinate progress indicator shows the actual progress of a task, which can be expressed as a percentage. In Flutter, you can achieve this by specifying the value property of CircularProgressIndicator.

The value should be a double ranging from 0.0 to 1.0, where:

  • 0.0 indicates no progress,
  • 1.0 indicates the task is complete.

Here’s an example of a determinate progress indicator:

import 'package:flutter/material.dart';
import 'dart:async';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  double progressValue = 0.0;

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

  void startProgress() {
    Timer.periodic(Duration(seconds: 1), (Timer timer) {
      setState(() {
        progressValue += 0.1;
        if (progressValue >= 1.0) {
          timer.cancel();
        }
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Determinate Progress Indicator'),
        ),
        body: Center(
          child: CircularProgressIndicator(
            value: progressValue,
            backgroundColor: Colors.grey[300],
            valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
            strokeWidth: 5.0,
          ),
        ),
      ),
    );
  }
}

In this example, the progress value increases every second by 0.1 until it reaches 1.0, where the indicator stops.

Customizing the Determinate Indicator

  • backgroundColor: Specifies the color of the area of the progress indicator that is not yet completed.
  • valueColor: Animates the color of the portion that represents progress. This can be an AlwaysStoppedAnimation to keep it static, or an animated value.
  • strokeWidth: Controls the thickness of the indicator.

CircularProgressIndicator Inside a Button

Sometimes you want to display a loading indicator inside a button, especially when the user clicks on a button that triggers a time-consuming task (e.g., login or file upload).

Here’s an example of a button that shows a circular progress indicator when pressed:

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  bool isLoading = false;

  void simulateLoading() async {
    setState(() {
      isLoading = true;
    });

    await Future.delayed(Duration(seconds: 3));

    setState(() {
      isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('CircularProgressIndicator in Button'),
        ),
        body: Center(
          child: ElevatedButton(
            onPressed: isLoading ? null : simulateLoading,
            child: isLoading
                ? CircularProgressIndicator(
                    color: Colors.white,
                  )
                : Text('Submit'),
          ),
        ),
      ),
    );
  }
}

In this example, when the user clicks the button, the button text changes to a loading spinner, indicating the process is in progress. After 3 seconds, the spinner disappears, and the button becomes clickable again.


CircularProgressIndicator with SizedBox

To control the size of the circular progress indicator, you can wrap it with a SizedBox. By default, the CircularProgressIndicator is quite small, but you can increase its size by placing it inside a SizedBox widget.

Example:

Center(
  child: SizedBox(
    height: 60.0,
    width: 60.0,
    child: CircularProgressIndicator(
      strokeWidth: 6.0,
    ),
  ),
)

Here, we use a SizedBox to define a 60×60 container for the circular progress indicator. You can adjust the height and width as needed.


Conclusion

The CircularProgressIndicator is a simple yet powerful widget in Flutter for displaying the progress of tasks, especially when feedback is required for users during long-running operations. Whether you need an indeterminate progress spinner or one that reflects real-time progress, the CircularProgressIndicator can be customized to fit your needs.

Key takeaways:

  • The CircularProgressIndicator is ideal for showing task progress in a circular format.
  • It supports both indeterminate and determinate states.
  • You can easily customize its color, stroke width, and size to suit your design requirements.

By using it effectively, you can enhance your app’s user experience by providing clear visual feedback during background tasks or lengthy operations.