flutter_main_button

A versatile and customizable Flutter button widget that supports both single and dual-button layouts with built-in loading states, icons, gradients, shadows, and extensive styling options.

Features

Single & Dual Layouts: Use as a single button or display two buttons side-by-side
🔄 Loading States: Built-in loading spinner with automatic state handling
🎨 Fully Customizable: Colors, borders, radius, padding, gradients, and shadows
📱 Icons & Widgets: Prefix/suffix icons OR custom widgets
🌈 Gradient Support: Linear and radial gradients
🎭 Custom Shapes: Rectangle or circle shapes
🖼️ Background Images: Decoration images support
👶 Custom Children: Replace button content with any widget
Accessibility: Proper disabled state handling
🎯 Type Safe: Strongly typed with comprehensive documentation

Designs

Alt text 1 Alt text 2 Alt text 3 Alt text 3

Installation

Add this to your package's pubspec.yaml file:

dependencies:
  flutter_main_button: ^1.1.0

Then run:

flutter pub get

Usage

Basic Single Button

import 'package:flutter_main_button/flutter_main_button.dart';

MainButton(
  text: "Submit",
  onPressed: () {
    print("Button pressed!");
  },
)

Button with Loading State

MainButton(
  text: "Register",
  isLoading: isSubmitting,
  onPressed: isSubmitting ? null : () async {
    setState(() => isSubmitting = true);
    await registerUser();
    setState(() => isSubmitting = false);
  },
)

Button with Icons

MainButton(
  text: "Download",
  prefixIcon: Icons.download,
  onPressed: () => handleDownload(),
)

// Or with suffix icon
MainButton(
  text: "Next",
  suffixIcon: Icons.arrow_forward,
  onPressed: () => goToNextPage(),
)

Button with Custom Widgets (NEW!)

Use prefix and suffix for completely custom widgets:

MainButton(
  text: "Premium",
  prefix: Container(
    padding: EdgeInsets.all(4),
    decoration: BoxDecoration(
      color: Colors.amber,
      shape: BoxShape.circle,
    ),
    child: Icon(Icons.star, size: 16, color: Colors.white),
  ),
  backgroundColor: Colors.purple,
  onPressed: () => upgradeToPremium(),
)

Button with Gradient (NEW!)

MainButton(
  text: "Gradient Button",
  gradient: LinearGradient(
    colors: [Colors.purple, Colors.blue],
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
  ),
  onPressed: () => handleAction(),
)

Button with Shadow (NEW!)

MainButton(
  text: "Elevated Button",
  backgroundColor: Colors.white,
  textColor: Colors.black87,
  boxShadow: [
    BoxShadow(
      color: Colors.black.withOpacity(0.2),
      blurRadius: 10,
      offset: Offset(0, 4),
    ),
  ],
  onPressed: () => handleAction(),
)

Circular Button with Shape (NEW!)

MainButton(
  prefixIcon: Icons.add,
  shape: BoxShape.circle,
  height: 60,
  width: 60,
  backgroundColor: Colors.blue,
  onPressed: () => addItem(),
)

Button with Background Image (NEW!)

MainButton(
  text: "Explore",
  image: DecorationImage(
    image: AssetImage('assets/pattern.png'),
    fit: BoxFit.cover,
    opacity: 0.3,
  ),
  textColor: Colors.white,
  onPressed: () => explore(),
)

Custom Child Widget (NEW!)

Replace the entire button content:

MainButton(
  child: Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      CircleAvatar(
        backgroundImage: NetworkImage('https://avatar.url'),
        radius: 16,
      ),
      SizedBox(width: 8),
      Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text('John Doe', style: TextStyle(fontWeight: FontWeight.bold)),
          Text('View Profile', style: TextStyle(fontSize: 12)),
        ],
      ),
    ],
  ),
  height: 70,
  onPressed: () => viewProfile(),
)

Custom Styling

MainButton(
  text: "Custom Button",
  backgroundColor: Colors.blue,
  textColor: Colors.white,
  borderRadius: BorderRadius.circular(30),
  border: Border.all(color: Colors.blue.shade700, width: 2),
  height: 50,
  fontSize: 18,
  fontWeight: FontWeight.bold,
  onPressed: () => handleAction(),
)

Dual Button Layout

Perfect for dialogs, confirmations, or navigation:

MainButton.dual(
  // First button (typically Cancel/Back)
  text: "Cancel",
  backgroundColor: Colors.grey.shade300,
  textColor: Colors.black,
  onPressed: () => Navigator.pop(context),

  // Second button (typically Confirm/Next)
  secondText: "Confirm",
  secondBackgroundColor: Colors.blue,
  secondOnPressed: () => handleConfirm(),
)

Dual Buttons with Gradients (NEW!)

MainButton.dual(
  text: "Cancel",
  gradient: LinearGradient(
    colors: [Colors.grey.shade400, Colors.grey.shade600],
  ),
  onPressed: () => cancel(),

  secondText: "Upgrade",
  secondGradient: LinearGradient(
    colors: [Colors.purple, Colors.pink],
  ),
  secondPrefix: Icon(Icons.star, size: 20),
  secondOnPressed: () => upgrade(),
)

Dual Buttons with Shadows (NEW!)

MainButton.dual(
  text: "Maybe",
  backgroundColor: Colors.white,
  textColor: Colors.black87,
  boxShadow: [
    BoxShadow(
      color: Colors.grey.shade300,
      blurRadius: 8,
      offset: Offset(0, 2),
    ),
  ],
  onPressed: () => handleMaybe(),

  secondText: "Yes!",
  secondBackgroundColor: Colors.green,
  secondBoxShadow: [
    BoxShadow(
      color: Colors.green.withOpacity(0.4),
      blurRadius: 12,
      offset: Offset(0, 4),
    ),
  ],
  secondOnPressed: () => handleYes(),
)

API Reference

MainButton (Single)

Core Properties

Parameter Type Default Description
text String? - Text displayed on the button
onPressed VoidCallback? - Callback when button is tapped
backgroundColor Color? Theme primary Button background color
textColor Color? Colors.white Text color
isLoading bool false Shows loading spinner when true
isDisabled bool false Disables button interactions
height double? 60 Button height
width double? double.infinity Button width (single layout only)

Icon Properties

Parameter Type Default Description
prefixIcon IconData? - Icon displayed before text
suffixIcon IconData? - Icon displayed after text
iconColor Color? textColor Icon color
iconSize double? 20 Icon size
iconSpacing double 8 Space between icon and text

Widget Properties (NEW!)

Parameter Type Default Description
prefix Widget? - Custom widget before text (overrides prefixIcon)
suffix Widget? - Custom widget after text (overrides suffixIcon)
child Widget? - Custom child widget (replaces entire content)

Styling Properties

Parameter Type Default Description
fontSize double? 16 Text font size
fontWeight FontWeight? FontWeight.w600 Text font weight
borderRadius BorderRadius? BorderRadius.circular(15) Button border radius
border BoxBorder? - Button border
padding EdgeInsetsGeometry? EdgeInsets.symmetric(horizontal: 16) Internal padding

Advanced Styling (NEW!)

Parameter Type Default Description
gradient Gradient? - Background gradient
boxShadow List<BoxShadow>? - Button shadow
image DecorationImage? - Background image
shape BoxShape? BoxShape.rectangle Button shape (rectangle/circle)

MainButton.dual (Dual Layout)

All single button properties, plus:

Second Button Properties

Parameter Type Default Description
secondText String? - Text for second button
secondOnPressed VoidCallback? - Callback for second button
secondBackgroundColor Color? Theme primary Second button background
secondTextColor Color? Colors.white Second button text color
secondIsLoading bool false Loading state for second button
secondIsDisabled bool false Disabled state for second button
secondPrefixIcon IconData? - Second button prefix icon
secondSuffixIcon IconData? - Second button suffix icon
secondIconColor Color? - Second button icon color
secondIconSize double? 20 Second button icon size
secondBorderRadius BorderRadius? - Second button border radius
secondBorder BoxBorder? - Second button border
buttonSpacing double 10 Space between two buttons

Second Button Widgets (NEW!)

Parameter Type Default Description
secondPrefix Widget? - Custom prefix widget for second button
secondSuffix Widget? - Custom suffix widget for second button
secondChild Widget? - Custom child for second button

Second Button Styling (NEW!)

Parameter Type Default Description
secondGradient Gradient? - Gradient for second button
secondBoxShadow List<BoxShadow>? - Shadow for second button
secondImage DecorationImage? - Background image for second button
secondShape BoxShape? - Shape for second button

Advanced Examples

1. Premium Upgrade Button

MainButton(
  height: 70,
  gradient: LinearGradient(
    colors: [Colors.purple.shade400, Colors.deepPurple.shade600],
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
  ),
  boxShadow: [
    BoxShadow(
      color: Colors.purple.withOpacity(0.5),
      blurRadius: 20,
      offset: Offset(0, 10),
    ),
  ],
  child: Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Icon(Icons.workspace_premium, color: Colors.amber, size: 28),
      SizedBox(width: 12),
      Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(
            'Upgrade to Premium',
            style: TextStyle(
              color: Colors.white,
              fontSize: 18,
              fontWeight: FontWeight.bold,
            ),
          ),
          Text(
            'Unlock all features',
            style: TextStyle(
              color: Colors.white70,
              fontSize: 12,
            ),
          ),
        ],
      ),
    ],
  ),
  onPressed: () => upgradeToPremium(),
)

2. Social Login Buttons

// Google
MainButton(
  text: "Continue with Google",
  backgroundColor: Colors.white,
  textColor: Colors.black87,
  border: Border.all(color: Colors.grey.shade300),
  prefix: Image.asset('assets/google_logo.png', height: 24),
  boxShadow: [
    BoxShadow(
      color: Colors.grey.shade200,
      blurRadius: 8,
      offset: Offset(0, 2),
    ),
  ],
  onPressed: () => signInWithGoogle(),
)

// Apple
MainButton(
  text: "Continue with Apple",
  backgroundColor: Colors.black,
  prefixIcon: Icons.apple,
  onPressed: () => signInWithApple(),
)

3. Floating Action Button Style

MainButton(
  shape: BoxShape.circle,
  height: 56,
  width: 56,
  backgroundColor: Colors.blue,
  prefixIcon: Icons.add,
  iconColor: Colors.white,
  iconSize: 24,
  boxShadow: [
    BoxShadow(
      color: Colors.black.withOpacity(0.3),
      blurRadius: 12,
      offset: Offset(0, 6),
    ),
  ],
  onPressed: () => createNew(),
)

4. Card-Style Button

MainButton(
  height: 80,
  backgroundColor: Colors.white,
  border: Border.all(color: Colors.grey.shade200),
  borderRadius: BorderRadius.circular(16),
  boxShadow: [
    BoxShadow(
      color: Colors.grey.shade100,
      blurRadius: 10,
      offset: Offset(0, 4),
    ),
  ],
  child: Padding(
    padding: EdgeInsets.symmetric(horizontal: 20),
    child: Row(
      children: [
        Container(
          padding: EdgeInsets.all(12),
          decoration: BoxDecoration(
            color: Colors.blue.shade50,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Icon(Icons.credit_card, color: Colors.blue),
        ),
        SizedBox(width: 16),
        Expanded(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'Add Payment Method',
                style: TextStyle(
                  fontWeight: FontWeight.bold,
                  fontSize: 16,
                  color: Colors.black87,
                ),
              ),
              Text(
                'Credit or debit card',
                style: TextStyle(
                  fontSize: 12,
                  color: Colors.grey.shade600,
                ),
              ),
            ],
          ),
        ),
        Icon(Icons.arrow_forward_ios, size: 16, color: Colors.grey),
      ],
    ),
  ),
  onPressed: () => addPaymentMethod(),
)

5. Call-to-Action Button

MainButton(
  height: 65,
  gradient: LinearGradient(
    colors: [Colors.orange.shade400, Colors.red.shade400],
  ),
  boxShadow: [
    BoxShadow(
      color: Colors.orange.withOpacity(0.5),
      blurRadius: 15,
      offset: Offset(0, 8),
    ),
  ],
  suffix: Container(
    padding: EdgeInsets.symmetric(horizontal: 8, vertical: 4),
    decoration: BoxDecoration(
      color: Colors.white.withOpacity(0.3),
      borderRadius: BorderRadius.circular(12),
    ),
    child: Text(
      '50% OFF',
      style: TextStyle(
        color: Colors.white,
        fontSize: 10,
        fontWeight: FontWeight.bold,
      ),
    ),
  ),
  child: Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      Icon(Icons.shopping_bag, color: Colors.white),
      SizedBox(width: 8),
      Text(
        'Shop Now',
        style: TextStyle(
          color: Colors.white,
          fontSize: 18,
          fontWeight: FontWeight.bold,
        ),
      ),
    ],
  ),
  onPressed: () => shopNow(),
)

6. Dual Gradient Buttons

MainButton.dual(
  height: 55,

  // Cancel button with subtle gradient
  text: "Not Now",
  gradient: LinearGradient(
    colors: [Colors.grey.shade300, Colors.grey.shade400],
  ),
  textColor: Colors.black87,
  onPressed: () => dismiss(),

  // Confirm button with vibrant gradient
  secondText: "Enable",
  secondGradient: LinearGradient(
    colors: [Colors.blue.shade400, Colors.purple.shade400],
    begin: Alignment.topLeft,
    end: Alignment.bottomRight,
  ),
  secondBoxShadow: [
    BoxShadow(
      color: Colors.blue.withOpacity(0.4),
      blurRadius: 12,
      offset: Offset(0, 6),
    ),
  ],
  secondSuffix: Icon(Icons.check_circle, size: 20, color: Colors.white),
  secondOnPressed: () => enable(),
)

Best Practices

  1. Loading States: Always disable the button when loading

    onPressed: isLoading ? null : handleAction
    
  2. Gradients: Use gradients sparingly for emphasis

    gradient: LinearGradient(colors: [primary, secondary])
    
  3. Shadows: Subtle shadows improve perceived depth

    boxShadow: [BoxShadow(color: Colors.black12, blurRadius: 8, offset: Offset(0, 4))]
    
  4. Custom Children: Use for complex button content

    child: Row(children: [...]) // Full control over content
    
  5. Circular Buttons: Set equal height/width with circle shape

    shape: BoxShape.circle, height: 56, width: 56
    

Common Use Cases

✅ Form submit buttons
✅ Dialog actions
✅ Navigation buttons
✅ Social authentication
✅ Call-to-action buttons
✅ Floating action buttons
✅ Card-style buttons
✅ Premium upgrade buttons

License

MIT License - see LICENSE file.

Support

⭐ Star on GitHub
🐛 Report issues
💬 Discussions

Libraries

flutter_main_button
A versatile Flutter button widget with support for single and dual-button layouts.