Skip to content

[Proposal] Allow Passing an AnimationController to CircularProgressIndicator #165741

@stan-at-work

Description

@stan-at-work

Pull-Request has been made here

Use case

Currently, CircularProgressIndicator does not support passing an AnimationController, making it difficult to customize its animation behavior. Adding this capability would allow developers to have more fine-grained control over animation timing, easing, and synchronization with other animations.


  • Custom easing and timing for progress animations

  • Ability to pause, reverse, or manipulate the progress animation dynamically

  • Synchronization with other animations in an application:

Ensuring multiple CircularProgressIndicator widgets share the same animation, even if inserted at different times in the widget tree

Screen.Recording.2025-09-15.at.3.32.37.PM.mp4

Credit to @dkwingsmt for creating this preview

Proposal

Introduce a controller parameter that accepts an AnimationController, similar to how other widgets support animated behaviors. If provided, the CircularProgressIndicator should use this controller instead of the default built-in animation.


Steps to fix this:

  • Add a nullable AnimationController to the class CircularProgressIndicator
final AnimationController? controller;
  • In _CircularProgressIndicatorState add a prop:
  late final AnimationController _fallbackController;
  • In initState in _CircularProgressIndicatorState

Set

  @override
  void initState() {
    super.initState();
    _fallbackController = AnimationController(duration: const Duration(milliseconds: _kIndeterminateCircularDuration), vsync: this);
    _controller = widget.controller ?? _fallbackController;
    if (widget.value == null) {
      _controller.repeat();
    }
  }
  • Set didUpdateWidget(...) to:
  @override
  void didUpdateWidget(CircularProgressIndicator oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.controller != widget.controller && _controller != widget.controller) {
      if (oldWidget.controller == null) _controller.dispose();
      _controller = widget.controller ?? _fallbackController;
    }
    if (widget.value == null && !_controller.isAnimating) {
      _controller.repeat();
    } else if (widget.value != null && _controller.isAnimating) {
      _controller.stop();
    }
  }
  • In dispose add:
if (widget.controller == null) _controller.dispose();

And there you have it:

result.mp4

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Issues that are less important to the Flutter projectc: new featureNothing broken; request for a new capabilityc: proposalA detailed proposal for a change to Flutterf: material designflutter/packages/flutter/material repository.frameworkflutter/packages/flutter repository. See also f: labels.good first issueRelatively approachable for first-time contributorsteam-frameworkOwned by Framework teamtriaged-frameworkTriaged by Framework team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions