Flutter Progress Bar: LinearProgressIndicator & CircularProgressIndicator

While your app loads data or uploads a file, users need a sign that something is happening. Flutter’s progress indicators give that sign, either as a moving bar or circle or as a measured percentage.

In this piece, I’ll build LinearProgressIndicator and CircularProgressIndicator examples, starting with a bar that fills as work completes.

What are Flutter progress indicators?

A Flutter progress indicator is a Material widget that shows an operation is underway.

LinearProgressIndicator draws along a line, while CircularProgressIndicator draws around an arc. Both widgets can show activity without a measurable completion amount, or show a fraction when your code knows how much work is done.

The value determines that state, not the shape.

  • LinearProgressIndicator draws along a line.
  • CircularProgressIndicator draws around an arc.

Choose indeterminate or determinate progress

An indeterminate indicator says work continues but gives no estimate of what remains. A determinate indicator communicates a measured fraction, so use it only when your operation can report completed work against a total.

LinearProgressIndicator(value: null)
LinearProgressIndicator(value: completed / total)

Flutter documents a non-null value between 0.0 and 1.0 as determinate progress, with the value increasing toward 1.0 as the task advances. A spinning or moving animation is not a substitute for a percentage when your code already knows the fraction.

Prerequisites

A Flutter project needs the SDK and a Material app for these examples.

RequirementFlutter SDK
Widget libraryMaterial

Step 1: Add a linear progress indicator

Use a linear indicator when the progress should read like a filled track, such as an upload row or a long-running page task. Leaving value null makes the widget show activity without claiming a measured percentage.

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(
    home: Scaffold(
      body: Center(
        child: Padding(
          padding: EdgeInsets.all(24),
          child: LinearProgressIndicator(
            color: Colors.blue,
            backgroundColor: Colors.black12,
            minHeight: 6,
            semanticsLabel: 'Loading data',
          ),
        ),
      ),
    ),
  ));
}

The track stays indeterminate because this constructor does not provide a value. The color and minimum height change its appearance, while the semantic label gives assistive technology a description of the activity.

Linear progress indicator in a Flutter app

Step 2: Show measured progress in a linear bar

The stateful example below advances a counter when you tap the button, then divides completed work by the total to calculate its fraction.

import 'package:flutter/material.dart';

void main() {
  runApp(const MaterialApp(home: ProgressDemo()));
}

class ProgressDemo extends StatefulWidget {
  const ProgressDemo({super.key});

  @override
  State<ProgressDemo> createState() => _ProgressDemoState();
}

class _ProgressDemoState extends State<ProgressDemo> {
  int completed = 0;
  final int total = 5;

  void advance() {
    setState(() {
      if (completed < total) completed++;
    });
  }

  @override
  Widget build(BuildContext context) {
    final double progress = completed / total;

    return Scaffold(
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(24),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              LinearProgressIndicator(
                value: progress,
                semanticsLabel: 'Tasks completed',
                semanticsValue: '$completed of $total',
              ),
              const SizedBox(height: 16),
              Text('$completed of $total tasks'),
              ElevatedButton(
                onPressed: completed < total ? advance : null,
                child: const Text('Complete one task'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

setState rebuilds with the incremented value, and the button disables at total while keeping the fraction between 0.0 and 1.0.

Step 3: Use a circular progress indicator

A circular indicator fits a compact status area where a track would take too much horizontal space. It uses the same value rule as the linear widget, so omit value for unknown progress or provide the measured fraction.

CircularProgressIndicator(
  value: 0.6,
  color: Colors.blue,
  backgroundColor: Colors.black12,
  strokeWidth: 7,
  semanticsLabel: 'Upload progress',
  semanticsValue: '60 percent',
)

This example represents a measured 60 percent, not an animation that merely suggests activity. The circular widget’s strokeWidth sets the arc thickness, and the semantic value provides a text equivalent for the visible fraction.

Circular progress indicator in a Flutter app

Verify the value and accessible label

Before connecting the indicator to a task, check that its visual message matches the state your code can measure.

  • Use a null value when the task has no meaningful completion estimate.
  • Use a fraction from 0.0 through 1.0 when completed work and total work are known.
  • Update the fraction from task state instead of incrementing it on a timer unrelated to the work.
  • Add a semantics label, and a semantics value when the completion amount is known.

Flutter’s API documents semanticsLabel and semanticsValue on both widgets. Keep the spoken description useful on its own, because color and animation do not explain the task to every user.

Linear progress indicator in a Flutter app

Keep the indicator honest about progress

Choose the shape for the space, then choose the value for the information your operation actually has. When a task exposes completed and total work, connect those numbers directly to the indicator.

final progress = completed / total;

Flutter progress bar questions

How do I create a circular progress bar in Flutter?

Add a CircularProgressIndicator from package:flutter/material.dart. Leave value null for indeterminate activity or pass a measured value from 0.0 to 1.0.

What does a null progress indicator value mean?

A null value displays indeterminate progress. It communicates that work is underway without stating how much remains.

How do I show a known percentage?

Divide completed work by total work and pass the resulting fraction as value. Keep the fraction between 0.0 and 1.0, with 1.0 representing completion.

Sushant Dhiman
Sushant Dhiman

Sushant Dhiman writes beginner-friendly Flutter and Dart tutorials covering widgets, layouts, navigation, dialogs, and state. He also covers Node.js, Express, and jQuery fundamentals.

Articles: 14