How to Create a Custom Progress Indicator with Animation in Flutter?
In this post, we’re going to be creating a custom progress indicator in Flutter. We’ll be using the animation controller from the Animation package. We’ll also be adding an underlying animation that will make our progress indicator look like it’s animating smoothly.

import 'package:flutter/material.dart'; class HomePage extends StatefulWidget { @override _HomePageState createState() => _HomePageState(); } class _HomePageState extends State<HomePage> { @override void initState() { super.initState(); } @override void dispose() { super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Progress Indicator'), ), body: SafeArea( child: Center( child: TwoFlyingDots(), ), ), ); } } class TwoFlyingDots extends StatelessWidget { @override Widget build(BuildContext context) { return Stack(children: [ FlyingDot(color: Colors.blue), FlyingDot(color: Colors.red, reverse: true) ]); } } class FlyingDot extends StatefulWidget { final Color? color; final bool? reverse; const FlyingDot({Key? key, this.color, this.reverse}) : super(key: key); @override _FlyingDotState createState() => _FlyingDotState(); } class _FlyingDotState extends State<FlyingDot> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<Offset> _position; late Animation<Offset> _position2; late Animation<Offset> _position3; late bool _reverse; double rangeX = 20.0; double rangeY = 10.0; @override void initState() { _reverse = widget.reverse ?? false; _controller = AnimationController(vsync: this, duration: Duration(seconds: 1)); Tween<Offset> start = _reverse ? Tween<Offset>(begin: Offset(-rangeX, 0), end: Offset(0, rangeY)) : Tween<Offset>(begin: Offset(rangeX, 0), end: Offset(0, rangeY)); Tween<Offset> middle = _reverse ? Tween<Offset>(begin: Offset(0, rangeY), end: Offset(rangeX, 0)) : Tween<Offset>(begin: Offset(0, rangeY), end: Offset(-rangeX, 0)); Tween<Offset> end = _reverse ? Tween<Offset>(begin: Offset(rangeX, 0), end: Offset(-rangeX, 0)) : Tween<Offset>(begin: Offset(-rangeX, 0), end: Offset(rangeX, 0)); _position = start.animate( CurvedAnimation( parent: _controller, curve: Interval(0, 0.25), ), ); _position2 = middle.animate( CurvedAnimation( parent: _controller, curve: Interval(0.25, 0.5), ), ); _position3 = end.animate( CurvedAnimation( parent: _controller, curve: Interval(0.5, 1), ), ); //run animation _controller.repeat(); super.initState(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _controller, builder: (context, child) { Offset offset = Offset.zero; if (_controller.value <= 0.25) { offset = _position.value; } else if (_controller.value <= 0.5) { offset = _position2.value; } else { offset = _position3.value; } return Transform.translate( offset: offset, child: Icon(Icons.circle, color: widget.color)); }); } @override void dispose() { _controller.dispose(); super.dispose(); } }