41 Passing Args
41.1 To Stateful
In Flutter, passing arguments to stateful widgets follows a specific pattern since you need to work with both the widget class and its associated state class. Here’s how to do it effectively:
41.1.1 Basic Pattern
The key is to pass arguments through the widget’s constructor and access them in the state class using widget.propertyName
.
class MyStatefulWidget extends StatefulWidget {
final String title;
final int initialCount;
final VoidCallback? onPressed;
const MyStatefulWidget({
? key,
Keythis.title,
required this.initialCount = 0,
this.onPressed,
}) : super(key: key);
@override
<MyStatefulWidget> createState() => _MyStatefulWidgetState();
State}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late int _counter;
@override
void initState() {
super.initState();
// Access widget properties using widget.propertyName
= widget.initialCount;
_counter }
@override
{
Widget build(BuildContext context) return Scaffold(
: AppBar(
appBar: Text(widget.title), // Accessing the title argument
title,
): Center(
body: Column(
child: MainAxisAlignment.center,
mainAxisAlignment: [
children'Count: $_counter'),
Text(
ElevatedButton(: () {
onPressed{
setState(() ++;
_counter});
.onPressed?.call(); // Using optional callback
widget},
: Text('Increment'),
child,
),
],
),
)
);}
}
41.1.2 Using the Widget
// In your parent widget
MyStatefulWidget(: 'Counter App',
title: 5,
initialCount: () {
onPressed'Button was pressed!');
print(},
)
41.1.3 Key Points
Access Pattern: In the state class, always use widget.propertyName
to access the arguments passed to the widget constructor.
Initialization: If you need to use the arguments for initial setup, do it in initState()
or directly in variable declarations.
Immutability: The widget properties are immutable, but you can use them to initialize mutable state variables.
41.1.4 Common Use Cases
Form with Initial Values:
class UserForm extends StatefulWidget {
final String initialName;
final String initialEmail;
const UserForm({
? key,
Keythis.initialName = '',
this.initialEmail = '',
}) : super(key: key);
@override
<UserForm> createState() => _UserFormState();
State}
class _UserFormState extends State<UserForm> {
late TextEditingController _nameController;
late TextEditingController _emailController;
@override
void initState() {
super.initState();
= TextEditingController(text: widget.initialName);
_nameController = TextEditingController(text: widget.initialEmail);
_emailController }
@override
void dispose() {
.dispose();
_nameController.dispose();
_emailControllersuper.dispose();
}
@override
{
Widget build(BuildContext context) return Column(
: [
children
TextField(: _nameController,
controller: InputDecoration(labelText: 'Name'),
decoration,
)
TextField(: _emailController,
controller: InputDecoration(labelText: 'Email'),
decoration,
),
]
);}
}
41.2 To Stateless
Passing arguments to stateless widgets is much simpler since there’s no separate state class to consider. You define the properties in the widget class and use them directly in the build
method.
41.2.1 Basic Pattern
class MyStatelessWidget extends StatelessWidget {
final String title;
final Color backgroundColor;
final VoidCallback? onTap;
final bool isEnabled;
const MyStatelessWidget({
? key,
Keythis.title,
required this.backgroundColor = Colors.blue,
this.onTap,
this.isEnabled = true,
}) : super(key: key);
@override
{
Widget build(BuildContext context) return GestureDetector(
: isEnabled ? onTap : null,
onTap: Container(
child: EdgeInsets.all(16),
padding: backgroundColor,
color: Text(
child,
title: TextStyle(
style: Colors.white,
color: 18,
fontSize,
),
),
)
);}
}
41.2.2 Using the Widget
// In your parent widget
MyStatelessWidget(: 'Click Me',
title: Colors.green,
backgroundColor: () {
onTap'Widget tapped!');
print(},
: true,
isEnabled )
41.2.3 Key Differences from Stateful Widgets
Direct Access: You access properties directly (e.g., title
) instead of using widget.title
.
No State Management: All data is immutable and passed through the constructor.
Simpler Structure: Only one class to manage, making the code more straightforward.
41.2.4 Common Examples
Custom Button:
class CustomButton extends StatelessWidget {
final String text;
final VoidCallback? onPressed;
final Color color;
final double borderRadius;
const CustomButton({
? key,
Keythis.text,
required this.onPressed,
this.color = Colors.blue,
this.borderRadius = 8.0,
}) : super(key: key);
@override
{
Widget build(BuildContext context) return ElevatedButton(
: onPressed,
onPressed: ElevatedButton.styleFrom(
style: color,
backgroundColor: RoundedRectangleBorder(
shape: BorderRadius.circular(borderRadius),
borderRadius,
),
): Text(text),
child
);}
}
User Card:
class UserCard extends StatelessWidget {
final String name;
final String email;
final String? avatarUrl;
final VoidCallback? onTap;
const UserCard({
? key,
Keythis.name,
required this.email,
required this.avatarUrl,
this.onTap,
}) : super(key: key);
@override
{
Widget build(BuildContext context) return Card(
: ListTile(
child: CircleAvatar(
leading: avatarUrl != null
backgroundImage? NetworkImage(avatarUrl!)
: null,
: avatarUrl == null
child? Icon(Icons.person)
: null,
,
): Text(name),
title: Text(email),
subtitle: onTap,
onTap,
)
);}
}
41.2.5 Comparison Summary
Stateless Widget:
├── Direct property access
├── No state management
├── Simpler structure
└── Immutable data only
Stateful Widget:
├── Access via widget.propertyName
├── Can maintain mutable state
├── More complex structure
└── Can change over time
When to Use Each:
- Stateless: When your widget only displays data and doesn’t need to change after being built
- Stateful: When your widget needs to maintain and update internal state based on user interactions or other events
The choice between stateless and stateful widgets depends on whether your widget needs to manage changing state, not on whether it accepts arguments.