loading_icon_button 1.0.2 copy "loading_icon_button: ^1.0.2" to clipboard
loading_icon_button: ^1.0.2 copied to clipboard

Loading button with icon and text in Flutter. It's working on all flutter platforms. It's a simple way to show a loading button.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:loading_icon_button/loading_icon_button.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Loading Icon Button Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: const LoadingButtonDemo(),
    );
  }
}

class LoadingButtonDemo extends StatefulWidget {
  const LoadingButtonDemo({Key? key}) : super(key: key);

  @override
  _LoadingButtonDemoState createState() => _LoadingButtonDemoState();
}

class _LoadingButtonDemoState extends State<LoadingButtonDemo> {
  // Progress tracking for the upload simulation
  double _uploadProgress = 0.0;
  bool _isUploading = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Loading Icon Button Demo'),
        elevation: 0,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            // 1. AutoLoadingButton (Material Design)
            _buildSectionHeader('1. AutoLoadingButton (Material)'),
            ElevatedAutoLoadingButton(
              onPressed: () => _simulateAutoOperation(),
              child: const Text('Auto Loading Button'),
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.all(Colors.purple),
                foregroundColor: WidgetStateProperty.all(Colors.white),
                shape: WidgetStateProperty.all(RoundedRectangleBorder(
                  borderRadius: BorderRadius.circular(12),
                )),
                elevation: WidgetStateProperty.all(4),
              ),
            ),
            const SizedBox(height: 30),

            // 2. Material LoadingButton with Builder Pattern
            _buildSectionHeader('2. Material LoadingButton (Builder)'),
            LoadingButtonBuilder.filled()
                .onPressed(() => _simulateUpload())
                .child(const Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(Icons.cloud_upload),
                    SizedBox(width: 8),
                    Text('Upload File'),
                  ],
                ))
                .style(LoadingButtonStyle(
                  backgroundColor: Colors.indigo,
                  foregroundColor: Colors.white,
                  loadingBackgroundColor: Colors.indigo.shade300,
                  successBackgroundColor: Colors.green,
                  errorBackgroundColor: Colors.red,
                  borderRadius: 8,
                  elevation: 2,
                ))
                .loadingText('Uploading...')
                .successText('Uploaded!')
                .errorText('Failed!')
                .build(),
            const SizedBox(height: 30),

            // 3. Elevated LoadingButton
            _buildSectionHeader('3. Elevated LoadingButton'),
            LoadingButton(
              type: ButtonType.elevated,
              onPressed: () => _simulateLogin(),
              child: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.login),
                  SizedBox(width: 8),
                  Text('Login'),
                ],
              ),
              style: const LoadingButtonStyle(
                backgroundColor: Colors.blue,
                foregroundColor: Colors.white,
                borderRadius: 8,
              ),
              loadingText: 'Logging in...',
              successText: 'Welcome!',
              errorText: 'Login failed',
              onError: (error) {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(
                    content: Text('Login error: $error'),
                    backgroundColor: Colors.red,
                  ),
                );
              },
            ),
            const SizedBox(height: 30),

            // 4. Outlined LoadingButton
            _buildSectionHeader('4. Outlined LoadingButton'),
            LoadingButtonBuilder.outlined()
                .onPressed(() => _simulateDownload())
                .child(const Row(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Icon(Icons.download),
                    SizedBox(width: 8),
                    Text('Download'),
                  ],
                ))
                .style(const LoadingButtonStyle(
                  borderColor: Colors.blue,
                  borderWidth: 2,
                  foregroundColor: Colors.blue,
                  borderRadius: 25,
                ))
                .loadingText('Downloading...')
                .successText('Downloaded!')
                .errorText('Failed!')
                .build(),
            const SizedBox(height: 30),

            // 5. Text Button Style
            _buildSectionHeader('5. Text Button Style'),
            LoadingButtonBuilder.text()
                .onPressed(() => _simulateTextAction())
                .child(const Text('Text Button'))
                .style(const LoadingButtonStyle(
                  foregroundColor: Colors.deepOrange,
                  padding: EdgeInsets.symmetric(horizontal: 20, vertical: 12),
                ))
                .loadingText('Loading...')
                .successText('Done!')
                .errorText('Error!')
                .build(),
            const SizedBox(height: 30),

            // 6. Icon Button Style
            _buildSectionHeader('6. Icon Button Style'),
            LoadingButton(
              type: ButtonType.icon,
              onPressed: () => _simulateIconAction(),
              child: const Icon(Icons.favorite),
              style: const LoadingButtonStyle(
                backgroundColor: Colors.red,
                foregroundColor: Colors.white,
                iconSize: 24,
              ),
              loadingWidget: const SizedBox(
                width: 24,
                height: 24,
                child: CircularProgressIndicator(
                  strokeWidth: 2,
                  valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                ),
              ),
              successWidget: const Icon(Icons.check, color: Colors.white),
              errorWidget: const Icon(Icons.error, color: Colors.white),
              onError: (error) {
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                    content: Text('Icon action failed'),
                    backgroundColor: Colors.red,
                  ),
                );
              },
            ),
            const SizedBox(height: 30),

            // 7. Custom Styled Button
            _buildSectionHeader('7. Custom Styled Button'),
            LoadingButton(
              type: ButtonType.filled,
              onPressed: () => _simulateCustomAction(),
              child: const Text('Custom Button'),
              style: LoadingButtonStyle(
                backgroundColor: Colors.deepPurple,
                foregroundColor: Colors.white,
                loadingBackgroundColor: Colors.deepPurple.shade300,
                successBackgroundColor: Colors.green,
                errorBackgroundColor: Colors.red,
                borderRadius: 30,
                elevation: 8,
                padding:
                    const EdgeInsets.symmetric(horizontal: 32, vertical: 16),
              ),
              loadingText: 'Processing...',
              successText: 'Success!',
              errorText: 'Try Again',
              width: double.infinity,
              height: 60,
              onStateChanged: (state) {
                print('Button state changed to: $state');
              },
            ),
            const SizedBox(height: 30),

            // 8. Submit Form Button
            _buildSectionHeader('8. Submit Form Button'),
            LoadingButton(
              type: ButtonType.elevated,
              onPressed: () => _simulateFormSubmit(),
              child: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.send),
                  SizedBox(width: 8),
                  Text('Submit Form'),
                ],
              ),
              style: const LoadingButtonStyle(
                backgroundColor: Colors.teal,
                foregroundColor: Colors.white,
                borderRadius: 12,
                elevation: 4,
              ),
              loadingWidget: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  SizedBox(
                    width: 16,
                    height: 16,
                    child: CircularProgressIndicator(
                      strokeWidth: 2,
                      valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                    ),
                  ),
                  SizedBox(width: 8),
                  Text('Submitting...'),
                ],
              ),
              successWidget: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.check_circle, size: 18),
                  SizedBox(width: 8),
                  Text('Submitted!'),
                ],
              ),
              errorWidget: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.error, size: 18),
                  SizedBox(width: 8),
                  Text('Failed!'),
                ],
              ),
            ),
            const SizedBox(height: 30),

            // 9. Button with Progress Indicator Below (0% to 100%)
            _buildSectionHeader('9. Button with Progress Indicator Below'),
            LoadingButton(
              type: ButtonType.elevated,
              onPressed: () => _simulateUploadWithProgress(),
              child: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.cloud_upload_outlined),
                  SizedBox(width: 8),
                  Text('Upload with Progress'),
                ],
              ),
              style: const LoadingButtonStyle(
                backgroundColor: Colors.green,
                foregroundColor: Colors.white,
                borderRadius: 12,
                elevation: 4,
              ),
              loadingWidget: CircularProgressIndicator(
                value: _uploadProgress, // Progress from 0.0 to 1.0
                strokeWidth: 4,
                valueColor: const AlwaysStoppedAnimation<Color>(Colors.green),
                backgroundColor: Colors.grey.shade300,
              ),
              successWidget: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.check_circle, size: 18),
                  SizedBox(width: 8),
                  Text('Uploaded!'),
                ],
              ),
              errorWidget: const Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.error, size: 18),
                  SizedBox(width: 8),
                  Text('Failed!'),
                ],
              ),
            ),
            const SizedBox(height: 40),
          ],
        ),
      ),
    );
  }

  Widget _buildSectionHeader(String title) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12),
      child: Text(
        title,
        style: TextStyle(
          fontSize: 18,
          fontWeight: FontWeight.w600,
          color: Colors.grey[700],
        ),
      ),
    );
  }

  // Simulation methods
  Future<void> _simulateLogin() async {
    await Future.delayed(const Duration(seconds: 2));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 3 == 0) {
      throw Exception('Invalid credentials');
    }
    // Success is automatic if no exception is thrown
  }

  Future<void> _simulateAutoOperation() async {
    await Future.delayed(const Duration(seconds: 2));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 3 == 0) {
      throw Exception('Auto operation failed');
    }
    // Success is automatic
  }

  Future<void> _simulateUpload() async {
    await Future.delayed(const Duration(seconds: 3));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 5 == 0) {
      throw Exception('Upload failed');
    }
    // Success is automatic
  }

  Future<void> _simulateDownload() async {
    await Future.delayed(const Duration(milliseconds: 2000));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 4 == 0) {
      throw Exception('Download failed');
    }
    // Success is automatic
  }

  Future<void> _simulateTextAction() async {
    await Future.delayed(const Duration(milliseconds: 1000));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 3 == 0) {
      throw Exception('Text action failed');
    }
  }

  Future<void> _simulateIconAction() async {
    await Future.delayed(const Duration(milliseconds: 800));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 4 == 0) {
      throw Exception('Icon action failed');
    }
  }

  Future<void> _simulateCustomAction() async {
    await Future.delayed(const Duration(milliseconds: 1500));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 3 == 0) {
      throw Exception('Custom action failed');
    }
  }

  Future<void> _simulateFormSubmit() async {
    await Future.delayed(const Duration(seconds: 2));

    // Simulate random success/failure
    if (DateTime.now().millisecondsSinceEpoch % 4 == 0) {
      throw Exception('Form submission failed');
    }
  }

  Future<void> _simulateUploadWithProgress() async {
    setState(() {
      _isUploading = true;
      _uploadProgress = 0.0;
    });

    try {
      // Simulate progress from 0% to 100% over 5 seconds
      const totalDuration = Duration(seconds: 5);
      const steps = 20; // Number of progress updates
      final stepDuration = totalDuration.inMilliseconds ~/ steps;

      for (int i = 1; i <= steps; i++) {
        await Future.delayed(Duration(milliseconds: stepDuration));
        setState(() {
          _uploadProgress = i / steps;
        });
      }

      // Simulate random failure after progress
      if (DateTime.now().millisecondsSinceEpoch % 3 == 0) {
        throw Exception('Upload failed after progress');
      }
      // Success is automatic if no exception
    } catch (e) {
      // The button will handle the error state automatically
      rethrow; // Rethrow to let the button catch it
    } finally {
      // Reset progress after completion (success or error)
      await Future.delayed(
          const Duration(seconds: 2)); // Wait for success/error display
      setState(() {
        _isUploading = false;
        _uploadProgress = 0.0;
      });
    }
  }
}
35
likes
160
points
619
downloads
screenshot

Publisher

verified publishermohesu.com

Weekly Downloads

Loading button with icon and text in Flutter. It's working on all flutter platforms. It's a simple way to show a loading button.

Homepage
Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter, rxdart

More

Packages that depend on loading_icon_button