Fundamentals - Widgets
The majority of people prefers symmetrical and simple displays so it seems worthwhile to try and find a way to construct a default template for defining the widgets. While testing, I have realized that there are 2 ways to construct a Widget and use as a wrapper for items.
The objective is to build a Widget, MyMenuBox
that acts like a wrapper around Icon Buttons to display at the main page (page where the user enters the app). The Icon Buttons navigates the user to the corresponding pages.
Method 1: Widget as a StatelessWidget class
This constructs the Widget using OOP design.
class MyMenuBox extends StatelessWidget {
final IconData iconData;
final String label;
final VoidCallback onPressed;
final Color foregroundColor = Colors.white;
final Color backgroundColor = Colors.transparent;
const MyMenuBox({
super.key,
required this.iconData,
required this.label,
required this.onPressed,
});
@override
Widget build(BuildContext context) {
return TextButton.icon(
style: TextButton.styleFrom(
foregroundColor: foregroundColor, // Text color
backgroundColor: backgroundColor, // Background color
// padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0),
//shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
//side: BorderSide(
// color: Colors.white.withOpacity(0.3),
//), // Light border for glossy look
elevation: 1,
),
onPressed: onPressed,
icon: Icon(iconData, size: 35, color: Colors.white),
label: Text(label, style: TextStyle(color: Colors.white)),
);
}
}
Method 2: Method 1: Stateful Widget using StatelessWidget Class
This constructs the Widget as standalone.
// Standalone Widget version of the CoverTextButton
Widget myMenuBox(IconData iconData, String label, VoidCallback onPressed) {
return TextButton.icon(
style: TextButton.styleFrom(
foregroundColor: Colors.white, // Text color
backgroundColor: Colors.white.withAlpha(1), // Background color
// padding: const EdgeInsets.symmetric(vertical: 16.0, horizontal: 32.0),
//shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
//side: BorderSide(
// color: Colors.white.withOpacity(0.3),
//), // Light border for glossy look
elevation: 1,
),
onPressed: onPressed,
icon: Icon(iconData, size: 35, color: Colors.white),
label: Text(label, style: TextStyle(color: Colors.white)),
);
}
Comparison
The table below highlights the key differences in usage between the methods.
Calling the Widget
CoverTextButton(
iconData: Icons.insights,
label: 'Begin Reading',
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const HomePage()),
);
},
myMenuBox(Icons.insights, 'Start Reading', () {
_navigateTo(context, const HomePage());
}),
Another Example
// Implementing myTemplate where the CosmicBackground() is an animated background. This is useful when you want the animated background to stay across navigated pages.
// MyTemplate as StatelessWidget
class MyTemplate extends StatelessWidget {
final Widget body;
const MyTemplate({super.key, required this.body});
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor:
Colors.transparent, // Make Scaffold background transparent
body: Stack(
children: [
const CosmicBackground(), // Cosmic background as default
body,
],
),
);
}
}
// myTemplate as standalone function
Widget myTemplate(body) {
return Scaffold(
backgroundColor: Colors.transparent,
body: Stack(children: [const CosmicBackground(), body]),
);
}
Summary
Method 1 (Class-Based Widget): Best for when your widget is part of a more structured and larger app, especially when you need state management, custom constructors, or default values. It’s ideal for complex, reusable widgets that may grow in functionality.
Method 2 (Function-Based Widget): Suitable for simpler, smaller widgets that don’t need complex logic or state management. It’s a great choice for quick, reusable UI elements that are not likely to change much.
Last updated