How to create Flutter Reusable Custom Widgets?
Flutter is a powerful framework for building mobile applications, offering a rich set of pre-built widgets to create beautiful and functional user interfaces. However, as your application grows, you may find yourself repeating the same widget configurations across different parts of your app. This is where reusable custom widgets come into play. By creating custom widgets, you can encapsulate common UI patterns and logic, making your code more maintainable, scalable, and easier to manage.
In this blog, we will explore the concept of reusable custom widgets in Flutter, understand why they are essential, and walk through the process of creating them with practical examples.
Why Reusable Custom Widgets?
Reusable custom widgets offer several advantages:
- Code Reusability: By creating a widget once and reusing it throughout your application, you can reduce redundancy and ensure consistency in your UI design.
- Maintainability: When you need to make changes to a UI component, you only need to update the custom widget, and the changes will be reflected everywhere it’s used.
- Modularity: Custom widgets encapsulate UI and behavior, making your code modular and easier to test.
- Simplification: They help in breaking down complex UIs into smaller, manageable components.
Understanding the Basics
Before diving into custom widgets, it’s essential to understand the basic building blocks of Flutter widgets. Widgets in Flutter can be broadly categorized into two types:
- StatelessWidget: A widget that does not require mutable state. It only depends on the configuration passed to it.
- StatefulWidget: A widget that maintains a mutable state. It can change over time in response to user interaction or other events.
Both StatelessWidget
and StatefulWidget
can be customized to create reusable components.

Creating a Simple Reusable Custom Widget
Let’s start with a basic example: a custom button widget. Instead of using Flutter’s built-in buttons everywhere, you can create your own button with a specific style that you can reuse throughout your app.
1. Custom Button Widget
import 'package:flutter/material.dart';
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback onPressed;
final Color color;
final double width;
final double height;
const CustomButton({
Key? key,
required this.text,
required this.onPressed,
this.color = Colors.blue,
this.width = 200,
this.height = 50,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
width: width,
height: height,
child: ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
primary: color,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
),
),
child: Text(
text,
style: TextStyle(fontSize: 16, color: Colors.white),
),
),
);
}
}
Explanation:
- The
CustomButton
class extendsStatelessWidget
, meaning it does not maintain any internal state. - The widget accepts several parameters, such as
text
,onPressed
,color
,width
, andheight
, allowing you to customize its appearance and behavior. - The
ElevatedButton
inside theContainer
is styled according to the provided parameters.
Usage Example:
import 'package:flutter/material.dart';
import 'custom_button.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Custom Button Example')),
body: Center(
child: CustomButton(
text: 'Click Me',
onPressed: () {
print('Button Pressed!');
},
color: Colors.green,
width: 250,
height: 60,
),
),
),
);
}
}
In this example, you can use CustomButton
throughout your app, and it will always have a consistent style. You can also easily tweak its appearance by passing different parameters.
Advanced Reusable Custom Widgets
Now, let’s take it a step further by creating a more complex custom widget that involves state management. Consider a custom widget that toggles between two states, such as a “like” button.
2. Custom Like Button Widget
import 'package:flutter/material.dart';
class LikeButton extends StatefulWidget {
final bool isLiked;
final VoidCallback onToggle;
const LikeButton({
Key? key,
required this.isLiked,
required this.onToggle,
}) : super(key: key);
@override
_LikeButtonState createState() => _LikeButtonState();
}
class _LikeButtonState extends State<LikeButton> {
late bool _isLiked;
@override
void initState() {
super.initState();
_isLiked = widget.isLiked;
}
void _toggleLike() {
setState(() {
_isLiked = !_isLiked;
widget.onToggle();
});
}
@override
Widget build(BuildContext context) {
return IconButton(
icon: Icon(
_isLiked ? Icons.favorite : Icons.favorite_border,
color: _isLiked ? Colors.red : Colors.grey,
),
onPressed: _toggleLike,
);
}
}
Explanation:
LikeButton
extendsStatefulWidget
because it needs to maintain and update its internal state (_isLiked
).- The
onToggle
callback allows the parent widget to respond to changes in the like state. - The
IconButton
displays a filled heart icon if the item is liked and an outlined heart if it’s not. When tapped, it toggles the like state.
Usage Example:
import 'package:flutter/material.dart';
import 'like_button.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Like Button Example')),
body: Center(
child: LikeButton(
isLiked: false,
onToggle: () {
print('Like button toggled!');
},
),
),
),
);
}
}
Here, the LikeButton
is used in a simple app to toggle a like state. You can easily integrate this widget wherever you need a like button, ensuring consistent behavior and appearance across your app.
Building Complex UIs with Custom Widgets
For larger apps, you can combine multiple custom widgets to create more complex UI components. For example, you could create a custom card widget that displays an image, a title, and a button, all using other custom widgets.
3. Custom Card Widget
import 'package:flutter/material.dart';
import 'custom_button.dart';
class CustomCard extends StatelessWidget {
final String imageUrl;
final String title;
final String description;
final VoidCallback onPressed;
const CustomCard({
Key? key,
required this.imageUrl,
required this.title,
required this.description,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
elevation: 5,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ClipRRect(
borderRadius: BorderRadius.vertical(top: Radius.circular(10)),
child: Image.network(imageUrl, height: 150, width: double.infinity, fit: BoxFit.cover),
),
Padding(
padding: const EdgeInsets.all(10.0),
child: Text(title, style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Text(description),
),
SizedBox(height: 10),
Padding(
padding: const EdgeInsets.all(10.0),
child: CustomButton(
text: 'Learn More',
onPressed: onPressed,
color: Colors.blueAccent,
),
),
],
),
);
}
}
Explanation:
- The
CustomCard
widget combines an image, text, and a button into a single reusable component. - It uses the previously created
CustomButton
widget, showcasing how you can build complex widgets by composing smaller, reusable components.
Usage Example:
import 'package:flutter/material.dart';
import 'custom_card.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: Text('Custom Card Example')),
body: Center(
child: CustomCard(
imageUrl: 'https://via.placeholder.com/150',
title: 'Flutter Custom Widgets',
description: 'Learn how to create reusable custom widgets in Flutter.',
onPressed: () {
print('Learn More pressed!');
},
),
),
),
);
}
}
This CustomCard
can be reused throughout your app wherever you need a card layout, ensuring that your UI remains consistent and your code remains clean and maintainable.
Best Practices for Creating Reusable Custom Widgets
- Keep Widgets Small and Focused: Each widget should have a single responsibility, making it easier to reuse and test.
- Use Parameters Wisely: Pass parameters to customize your widgets, allowing them to be flexible and adaptable to different use cases.
- Leverage Composition: Build complex widgets by combining simpler ones, keeping your code modular and organized.
- Document Your Widgets: Clearly document what each custom widget does, including its parameters and expected behavior.
Conclusion
Creating reusable custom widgets in Flutter is a powerful way to manage your UI code effectively. By encapsulating common patterns and behaviors into custom widgets, you can ensure that your application is more maintainable, scalable, and consistent. Whether you’re building simple buttons or complex cards, custom widgets allow you to write cleaner, more modular code that’s easier to maintain and extend.
Start experimenting with custom widgets in your Flutter projects, and see how they can streamline your development process and improve the quality of your code!
Show Your Support


4 Responses