Question #336MediumWidgets & UI

Can you explain the process of creating custom widgets in Flutter?

#widget#custom#composition

Answer

Overview

Creating custom widgets in Flutter means extracting reusable UI components into their own widget classes. Flutter encourages a composition-based approach — breaking down UI into small, focused, reusable widgets.


Two Types of Custom Widgets

TypeWhen to Use
text
StatelessWidget
No mutable state
text
StatefulWidget
Has internal mutable state

Custom StatelessWidget

dart
class CustomCard extends StatelessWidget {
  final String title;
  final String subtitle;
  final IconData icon;
  final Color color;
  final VoidCallback? onTap;

  const CustomCard({
    super.key,
    required this.title,
    required this.subtitle,
    required this.icon,
    this.color = Colors.blue,
    this.onTap,
  });

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Card(
        elevation: 4,
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
        child: Padding(
          padding: EdgeInsets.all(16),
          child: Row(
            children: [
              CircleAvatar(
                backgroundColor: color.withOpacity(0.2),
                child: Icon(icon, color: color),
              ),
              SizedBox(width: 16),
              Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(title, style: TextStyle(fontWeight: FontWeight.bold)),
                  Text(subtitle, style: TextStyle(color: Colors.grey)),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// Usage
CustomCard(
  title: 'Revenue',
  subtitle: '₹1,23,456',
  icon: Icons.currency_rupee,
  color: Colors.green,
  onTap: () => print('Tapped'),
)

Custom StatefulWidget

dart
class ToggleButton extends StatefulWidget {
  final String label;
  final ValueChanged<bool>? onChanged;

  const ToggleButton({super.key, required this.label, this.onChanged});

  
  State<ToggleButton> createState() => _ToggleButtonState();
}

class _ToggleButtonState extends State<ToggleButton> {
  bool _isActive = false;

  
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        setState(() => _isActive = !_isActive);
        widget.onChanged?.call(_isActive);
      },
      child: AnimatedContainer(
        duration: Duration(milliseconds: 200),
        padding: EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        decoration: BoxDecoration(
          color: _isActive ? Colors.blue : Colors.grey[200],
          borderRadius: BorderRadius.circular(20),
        ),
        child: Text(
          widget.label,
          style: TextStyle(
            color: _isActive ? Colors.white : Colors.black,
          ),
        ),
      ),
    );
  }
}

Best Practices

  • ✅ Use
    text
    const
    constructor when possible for performance
  • ✅ Use
    text
    super.key
    in the constructor
  • ✅ Keep widgets focused — one responsibility per widget
  • ✅ Accept callbacks (
    text
    VoidCallback
    ,
    text
    ValueChanged
    ) instead of handling logic inside
  • ✅ Use
    text
    final
    for all properties
  • ❌ Avoid business logic inside widgets — keep it in ViewModels/Controllers

Key Principle: In Flutter, everything is a widget. Prefer extracting repeated UI into named custom widgets rather than building everything inline.