Skip to Content
ComponentsPrimitiveCircularProgress

PrimitiveCircularProgress

A circular progress indicator that supports both indeterminate (spinning) and determinate (value-based) modes. This component demonstrates how to combine CustomPaint with AnimationController for smooth animations.

Overview

PrimitiveCircularProgress renders a circular progress bar using CustomPaint. It can either spin indefinitely to indicate loading or show a specific percentage of completion.

Primitives Used: CustomPaint, Canvas, AnimationController

Primitive Circular Progress ShowcaseOpen in new tab ↗

Basic Usage

import 'package:primitive_ui/primitive_ui.dart'; // Indeterminate (spinning) PrimitiveCircularProgress() // Determinate (50%) PrimitiveCircularProgress( value: 0.5, )

API Reference

Constructor

PrimitiveCircularProgress({ Key? key, double? value, Color color = const Color(0xFF2196F3), double strokeWidth = 4.0, double size = 40.0, String? semanticsLabel, })

Parameters

ParameterTypeRequiredDefaultDescription
valuedouble?✗nullThe value of the progress (0.0 to 1.0). If null, the indicator is indeterminate.
colorColor✗Color(0xFF2196F3)The color of the progress indicator.
strokeWidthdouble✗4.0The width of the line used to draw the circle.
sizedouble✗40.0The diameter of the progress indicator.
semanticsLabelString?✗nullAccessibility label (defaults to "Loading" or "Progress")

Examples

Modes

PrimitiveCircularProgress( color: Colors.blue, )

Custom Styling

PrimitiveCircularProgress( value: 0.3, color: Colors.orange, size: 60.0, strokeWidth: 8.0, )

Loading State Example

class LoadingButton extends StatelessWidget { final bool isLoading; final VoidCallback onTap; LoadingButton({required this.isLoading, required this.onTap}); @override Widget build(BuildContext context) { return GestureDetector( onTap: isLoading ? null : onTap, child: PrimitiveCard( color: Colors.blue, child: Center( child: isLoading ? PrimitiveCircularProgress( color: Colors.white, size: 24, strokeWidth: 3, ) : Text('Submit', style: TextStyle(color: Colors.white)), ), ), ); } }

Implementation Details

The PrimitiveCircularProgress is implemented using:

  1. AnimationController: Used primarily for the indeterminate state to rotate the visual.
  2. AnimatedBuilder: Listens to the controller and triggers repaints.
  3. CustomPaint:
    • Determinate: Draws a partial arc based on the value property (e.g., 2 * pi * value).
    • Indeterminate: Rotates a fixed-length arc based on the animation value.

Drawing Logic

if (value != null) { // Determinate: draw arc based on value double sweepAngle = 2 * pi * value!.clamp(0.0, 1.0); canvas.drawArc(rect, -pi / 2, sweepAngle, false, paint); } else { // Indeterminate: rotate fixed arc // ... rotation logic ... canvas.drawArc(rect, startAngle, sweepAngle, false, paint); }

Limitations

Known Limitations:

  • The indeterminate animation is a simple rotation; it does not currently implement the complex “worm” expansion/contraction effect of the Material Design spinner to keep the primitive implementation simple.

Source Code

View the complete implementation in the GitHub repository .

Last updated on