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
| Type | When to Use |
|---|---|
text | No mutable state |
text | Has internal mutable state |
Custom StatelessWidget
dartclass 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
dartclass 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 constructor when possible for performancetext
const - ✅ Use in the constructortext
super.key - ✅ Keep widgets focused — one responsibility per widget
- ✅ Accept callbacks (,text
VoidCallback) instead of handling logic insidetextValueChanged - ✅ Use for all propertiestext
final - ❌ 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.