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.
colorColorColor(0xFF2196F3)The color of the progress indicator.
strokeWidthdouble4.0The width of the line used to draw the circle.
sizedouble40.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