Flutter: Creating Our Own Widgets
personAhmet Balaman
calendar_today
FlutterWidgetStatelessWidgetStatefulWidgetConstructor
Creating Our Own Widgets in Flutter
By creating your own widgets in Flutter, you can make your code more modular, readable and reusable. You can create customizable widgets by taking parameters using constructors.
Why Should We Create Custom Widgets?
- Prevent Code Repetition: To use the same structure in different places
- Readability: Express complex structures with simple names
- Easy Maintenance: Make changes from a single place
- Testability: Test widgets separately
Live Demo: Creating Custom Widgets
Learn how to create your own widgets interactively:
Custom Widget with StatelessWidget
StatelessWidget is used for widgets without state (whose state doesn't change).
Basic Structure
class MyCustomWidget extends StatelessWidget {
const MyCustomWidget({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(16),
child: Text('My Custom Widget'),
);
}
}Usage
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: MyCustomWidget(),
),
),
);
}
}Parameters with Constructor
You can take parameters in the constructor to make your widget customizable.
Simple Parameters
class CustomButton extends StatelessWidget {
final String text;
final Color color;
final VoidCallback onPressed;
const CustomButton({
Key? key,
required this.text,
required this.color,
required this.onPressed,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
padding: EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
child: Text(text),
);
}
}Usage
CustomButton(
text: 'Save',
color: Colors.blue,
onPressed: () {
print('Saved');
},
)Required and Optional Parameters
Required Parameters
class UserCard extends StatelessWidget {
final String name;
final String email;
const UserCard({
Key? key,
required this.name, // Required
required this.email, // Required
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(name, style: TextStyle(fontWeight: FontWeight.bold)),
SizedBox(height: 4),
Text(email, style: TextStyle(color: Colors.grey)),
],
),
),
);
}
}Optional Parameters
class CustomCard extends StatelessWidget {
final String title;
final String? subtitle; // Optional (nullable)
final Color color;
const CustomCard({
Key? key,
required this.title,
this.subtitle, // Optional
this.color = Colors.blue, // Default value
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
color: color,
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(title, style: TextStyle(fontSize: 18)),
if (subtitle != null) // Show if exists
Text(subtitle!, style: TextStyle(fontSize: 14)),
],
),
),
);
}
}Usage:
CustomCard(title: 'Title') // No subtitle
CustomCard(
title: 'Title',
subtitle: 'Subtitle',
color: Colors.green,
)Widget Composition
You can break complex widgets into smaller parts.
Example: Blog Post Card
class BlogPostCard extends StatelessWidget {
final String title;
final String author;
final String date;
final String excerpt;
const BlogPostCard({
Key? key,
required this.title,
required this.author,
required this.date,
required this.excerpt,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
margin: EdgeInsets.all(8),
child: Padding(
padding: EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildHeader(),
SizedBox(height: 12),
_buildTitle(),
SizedBox(height: 8),
_buildExcerpt(),
],
),
),
);
}
Widget _buildHeader() {
return Row(
children: [
CircleAvatar(
child: Text(author[0]),
),
SizedBox(width: 8),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(author, style: TextStyle(fontWeight: FontWeight.bold)),
Text(date, style: TextStyle(fontSize: 12, color: Colors.grey)),
],
),
],
);
}
Widget _buildTitle() {
return Text(
title,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
);
}
Widget _buildExcerpt() {
return Text(
excerpt,
style: TextStyle(color: Colors.grey[700]),
maxLines: 3,
overflow: TextOverflow.ellipsis,
);
}
}Best Practices
- Use const Constructor:
const MyWidget({Key? key}) : super(key: key);- Give Meaningful Names:
// ✅ Good
class UserProfileCard extends StatelessWidget { ... }
// ❌ Bad
class Widget1 extends StatelessWidget { ... }Single Responsibility Principle:
Each widget should do one thing.Write Documentation:
/// Card widget that displays user information.
///
/// [name] and [email] parameters are required.
/// [avatarUrl] parameter is optional.
class UserCard extends StatelessWidget { ... }Summary
Creating custom widgets:
- Extends
StatelessWidgetorStatefulWidget - Takes parameters in constructor
- Required parameters are specified with
required - Default values can be assigned
- Flexible widgets are made with
childparameter - Complex structures are simplified with composition
Benefits:
- Prevents code repetition
- Increases readability
- Makes maintenance easier
- Increases testability
- Provides reusability
Creating widgets in Flutter is the foundation of writing clean and maintainable code. Consider turning every repeating structure into a widget!