In this tutorial, we’ll learn how to create an animated progress button in Flutter. This button will transform into a loading indicator when pressed, providing a smooth and engaging user experience.
## Step 1: Create the AnimatedProgressButton Widget
First, we’ll create a custom widget called `AnimatedProgressButton`. This widget will handle the animation and state changes of our button.
This widget uses an `AnimatedContainer` to smoothly transition between the button and loading states. When `isLoading` is true, the button shrinks and displays a `CircularProgressIndicator`.
class AnimatedProgressButton extends StatelessWidget { final VoidCallback onPressed; final bool isLoading; final Widget child; const AnimatedProgressButton({ Key? key, required this.onPressed, required this.isLoading, required this.child, }) : super(key: key); @override Widget build(BuildContext context) { return AnimatedContainer( duration: const Duration(milliseconds: 300), width: isLoading ? 50 : 200, height: 50, child: ElevatedButton( onPressed: isLoading ? null : onPressed, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), padding: EdgeInsets.zero, ), child: Center( child: isLoading ? const SizedBox( width: 30, height: 30, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : child, ), ), ); } }
## Step 2: Implement the Button in Your Page
Now, let’s see how to use this `AnimatedProgressButton` in a simple page.
full code:
import 'package:flutter/material.dart'; class NormalPage extends StatefulWidget { const NormalPage({Key? key}) : super(key: key); @override State<NormalPage> createState() => _NormalPageState(); } class _NormalPageState extends State<NormalPage> { bool _isLoading = false; void _startLoading() { setState(() { _isLoading = true; }); // Simulate a task Future.delayed(const Duration(seconds: 3), () { setState(() { _isLoading = false; }); }); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text("Animated Progress Button"), leading: IconButton( icon: const Icon(Icons.menu), onPressed: () {}, ), actions: <Widget>[ IconButton( icon: const Icon(Icons.comment), onPressed: () {}, ), //IconButton IconButton( icon: const Icon(Icons.settings), onPressed: () {}, ), //IconButton ], elevation: 1, ), body: Center( child: AnimatedProgressButton( onPressed: _startLoading, isLoading: _isLoading, child: const Text('Submit'), ), ), ); } } class AnimatedProgressButton extends StatelessWidget { final VoidCallback onPressed; final bool isLoading; final Widget child; const AnimatedProgressButton({ Key? key, required this.onPressed, required this.isLoading, required this.child, }) : super(key: key); @override Widget build(BuildContext context) { return AnimatedContainer( duration: const Duration(milliseconds: 300), width: isLoading ? 50 : 200, height: 50, child: ElevatedButton( onPressed: isLoading ? null : onPressed, style: ElevatedButton.styleFrom( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(30), ), padding: EdgeInsets.zero, ), child: Center( child: isLoading ? const SizedBox( width: 30, height: 30, child: CircularProgressIndicator( color: Colors.white, strokeWidth: 2, ), ) : child, ), ), ); } }