Button Loading FX 🎨

A Flutter package that provides beautiful, customizable loading animations for buttons. Transform boring loading states into delightful user experiences with smooth animations and progress indicators!

pub package License: MIT

✨ Features

  • 🎭 Multiple Loading Animations: Pulsing, liquid splash, circular progress
  • 📊 Progress Tracking: Real-time progress display with percentage
  • 🎨 Fully Customizable: Colors, sizes, stroke widths
  • 🚀 Easy to Use: Drop-in replacement for CircularProgressIndicator
  • 📦 Lightweight: Zero dependencies, maximum performance
  • 🎯 Type-Safe: Enum-based animation selection

🎬 Available Animations

1. Pulsing Animation

Smooth expanding and contracting circles - perfect for elegant loading states.

LoadingButtonStudio(
  animation: ButtonAnimationEffect.pulsing,
  effectColor: Colors.white,
)

2. Liquid Splash Animation

Water droplet effect with animated splashes - great for playful, dynamic interfaces.

LoadingButtonStudio(
  animation: ButtonAnimationEffect.liquidSplash,
  effectColor: Colors.white,
)

3. Circular Progress (NEW! 🎉)

Progress indicator with dual colors showing real-time percentage - ideal for downloads, uploads, or any task with measurable progress.

LoadingButtonStudio(
  animation: ButtonAnimationEffect.circularProgress,
  progress: 0.6, // 0.0 to 1.0 (60%)
  effectColor: Colors.blue,
  inactiveColor: Colors.grey.shade300,
)

4. Spinner Loader (NEW! 🎉)

A modern rotating spinner with radiating lines that fade in and out.

LoadingButtonStudio(
  animation: ButtonAnimationEffect.spinner,
  effectColor: Colors.orange,
)

5. Dots Loader (NEW! 🎉)

Classic rotating dots with one highlighted dot moving in a circle.

LoadingButtonStudio(
  animation: ButtonAnimationEffect.dots,
  effectColor: Colors.purple,
)

6. Linear Progress (NEW! 🎉)

A horizontal progress bar that fits perfectly in wide buttons.

LoadingButtonStudio(
  animation: ButtonAnimationEffect.linearProgress,
  progress: 0.4, // 40%
  effectColor: Colors.white,
  inactiveColor: Colors.blue.shade200,
  width: 120, // Customizable width
)

📦 Installation

Add to your pubspec.yaml:

dependencies:
  button_loading_fx: ^0.0.3

Then run:

flutter pub get

🚀 Quick Start

Import the package:

import 'package:button_loading_fx/button_loading_fx.dart';

Basic Loading Animation

class MyButton extends StatefulWidget {
  @override
  State<MyButton> createState() => _MyButtonState();
}

class _MyButtonState extends State<MyButton> {
  bool _isLoading = false;

  Future<void> _handlePress() async {
    setState(() => _isLoading = true);
    
    // Your async operation
    await Future.delayed(Duration(seconds: 2));
    
    setState(() => _isLoading = false);
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _isLoading ? null : _handlePress,
      child: _isLoading
          ? LoadingButtonStudio(
              animation: ButtonAnimationEffect.pulsing,
              effectColor: Colors.white,
            )
          : Text('Submit'),
    );
  }
}

Progress-Based Loading (Downloads, Uploads)

class DownloadButton extends StatefulWidget {
  @override
  State<DownloadButton> createState() => _DownloadButtonState();
}

class _DownloadButtonState extends State<DownloadButton> {
  bool _isDownloading = false;
  double _progress = 0.0;

  Future<void> _startDownload() async {
    setState(() => _isDownloading = true);
    
    // Simulate download with progress updates
    for (double i = 0; i <= 1.0; i += 0.02) {
      await Future.delayed(Duration(milliseconds: 50));
      setState(() => _progress = i);
    }
    
    setState(() => _isDownloading = false);
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _isDownloading ? null : _startDownload,
      style: ElevatedButton.styleFrom(backgroundColor: Colors.green),
      child: _isDownloading
          ? LoadingButtonStudio(
              animation: ButtonAnimationEffect.circularProgress,
              progress: _progress,
              effectColor: Colors.white,
              inactiveColor: Colors.green.shade200,
              size: 28,
            )
          : Row(
              mainAxisSize: MainAxisSize.min,
              children: [
                Icon(Icons.download),
                SizedBox(width: 8),
                Text('Download'),
              ],
            ),
    );
  }
}

📖 Detailed Examples

Pulsing Animation in Login Button

ElevatedButton(
  onPressed: _isLoading ? null : _login,
  style: ElevatedButton.styleFrom(
    backgroundColor: Colors.blue,
    padding: EdgeInsets.symmetric(horizontal: 32, vertical: 16),
  ),
  child: _isLoading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.pulsing,
          effectColor: Colors.white,
          size: 24,
        )
      : Text('Login', style: TextStyle(fontSize: 16)),
)

Liquid Splash in Submit Button

ElevatedButton(
  onPressed: _isLoading ? null : _submit,
  child: _isLoading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.liquidSplash,
          effectColor: Colors.white,
          size: 24,
        )
      : Text('Submit Form'),
)

Circular Progress in Upload Button

ElevatedButton(
  onPressed: _isUploading ? null : _upload,
  style: ElevatedButton.styleFrom(backgroundColor: Colors.orange),
  child: _isUploading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.circularProgress,
          progress: _uploadProgress,
          effectColor: Colors.orange,
          inactiveColor: Colors.orange.shade100,
          size: 30,
          strokeWidth: 4,
          showPercentage: true, // Shows "60%"
        )
      : Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(Icons.cloud_upload),
            SizedBox(width: 8),
            Text('Upload Photo'),
          ],
        ),
)

Linear Progress in a Button

ElevatedButton(
  onPressed: _isUploading ? null : _upload,
  style: ElevatedButton.styleFrom(backgroundColor: Colors.blue),
  child: _isUploading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.linearProgress,
          progress: _progress,
          effectColor: Colors.white,
          inactiveColor: Colors.blue.shade200,
        )
      : Text('Upload File'),
)

Spinner in a Login Button

ElevatedButton(
  onPressed: _isLoading ? null : _login,
  child: _isLoading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.spinner,
          effectColor: Colors.deepPurple,
          size: 24,
        )
      : Text('Login'),
)

Floating Action Button

FloatingActionButton(
  onPressed: _isLoading ? null : _handleAction,
  child: _isLoading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.liquidSplash,
          effectColor: Colors.white,
          size: 32,
        )
      : Icon(Icons.add),
)

Text Button

TextButton(
  onPressed: _isLoading ? null : _cancel,
  child: _isLoading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.pulsing,
          effectColor: Colors.blue,
          size: 20,
        )
      : Text('Cancel'),
)

🎨 Customization

LoadingButtonStudio API

Property Type Default Description
animation ButtonAnimationEffect pulsing Animation type (pulsing, liquidSplash, circularProgress)
effectColor Color Colors.white Primary/active color of animation
inactiveColor Color? null Background color (circularProgress only)
strokeWidth double 2.0 Width of strokes/rings
size double 20.0 Size of the loading indicator
width double 100.0 Width (LinearProgress only)
duration Duration? null Animation duration (auto if null)
progress double? null Progress value 0.0-1.0 (circularProgress only)
showPercentage bool true Show percentage text (circularProgress only)

ButtonAnimationEffect Enum

enum ButtonAnimationEffect {
  pulsing,          // Smooth scaling circles
  liquidSplash,     // Water droplet animation
  circularProgress, // Progress ring with percentage
  spinner,        // NEW
  dots,           // NEW
  linearProgress,  // NEW
}

Color Customization

// Match your brand
LoadingButtonStudio(
  animation: ButtonAnimationEffect.pulsing,
  effectColor: Theme.of(context).primaryColor,
)

// Custom progress colors
LoadingButtonStudio(
  animation: ButtonAnimationEffect.circularProgress,
  progress: _progress,
  effectColor: Colors.green,           // Active progress
  inactiveColor: Colors.green.shade100, // Background ring
)

Size Recommendations

// Small buttons (TextButton)
size: 16-18

// Medium buttons (ElevatedButton)
size: 20-24

// Large buttons
size: 28-32

// FAB
size: 32-36

💡 Real-World Use Cases

File Download with Progress

Future<void> _download() async {
  setState(() => _isDownloading = true);
  
  // Your actual download logic with progress callbacks
  await Dio().download(
    fileUrl,
    savePath,
    onReceiveProgress: (received, total) {
      setState(() {
        _progress = received / total;
      });
    },
  );
  
  setState(() => _isDownloading = false);
}

// Button
ElevatedButton(
  onPressed: _isDownloading ? null : _download,
  child: _isDownloading
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.circularProgress,
          progress: _progress,
          effectColor: Colors.white,
          size: 28,
        )
      : Text('Download'),
)

Form Submission

Future<void> _submitForm() async {
  setState(() => _isSubmitting = true);
  
  try {
    await api.submitForm(formData);
    Navigator.pop(context);
  } catch (e) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(content: Text('Error: $e')),
    );
  } finally {
    setState(() => _isSubmitting = false);
  }
}

// Button
ElevatedButton(
  onPressed: _isSubmitting ? null : _submitForm,
  child: _isSubmitting
      ? LoadingButtonStudio(
          animation: ButtonAnimationEffect.pulsing,
          effectColor: Colors.white,
        )
      : Text('Submit'),
)

🎯 Best Practices

1. Always Disable Button While Loading

onPressed: _isLoading ? null : _handlePress,

2. Match Colors to Your Theme

LoadingButtonStudio(
  effectColor: Theme.of(context).primaryColor,
)

3. Provide User Feedback

ScaffoldMessenger.of(context).showSnackBar(
  SnackBar(content: Text('Action completed!')),
);

4. Handle Errors Gracefully

try {
  setState(() => _isLoading = true);
  await performAction();
} catch (e) {
  // Show error
} finally {
  setState(() => _isLoading = false);
}

5. Choose the Right Animation

  • Pulsing: Professional apps (banking, business, forms)
  • Liquid Splash: Fun apps (games, social media, creative tools)
  • Circular Progress: Downloads, uploads, installations, data processing

🤝 Contributing

Contributions are welcome! Please read our Contributing Guidelines.

📝 Changelog

See CHANGELOG.md for version history.

📄 License

MIT License - see the LICENSE file for details.

📧 Support


Made with ❤️ for the Flutter community

Libraries

button_loading_fx
A collection of beautiful, customizable loading animations for Flutter buttons