48 Passing Args
48.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:
48.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? key,
required this.title,
this.initialCount = 0,
this.onPressed,
}) : super(key: key);
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
late int _counter;
@override
void initState() {
super.initState();
// Access widget properties using widget.propertyName
_counter = widget.initialCount;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title), // Accessing the title argument
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Count: $_counter'),
ElevatedButton(
onPressed: () {
setState(() {
_counter++;
});
widget.onPressed?.call(); // Using optional callback
},
child: Text('Increment'),
),
],
),
),
);
}
}48.1.2 Using the Widget
// In your parent widget
MyStatefulWidget(
title: 'Counter App',
initialCount: 5,
onPressed: () {
print('Button was pressed!');
},
)48.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.
48.1.4 Common Use Cases
Form with Initial Values:
class UserForm extends StatefulWidget {
final String initialName;
final String initialEmail;
const UserForm({
Key? key,
this.initialName = '',
this.initialEmail = '',
}) : super(key: key);
@override
State<UserForm> createState() => _UserFormState();
}
class _UserFormState extends State<UserForm> {
late TextEditingController _nameController;
late TextEditingController _emailController;
@override
void initState() {
super.initState();
_nameController = TextEditingController(text: widget.initialName);
_emailController = TextEditingController(text: widget.initialEmail);
}
@override
void dispose() {
_nameController.dispose();
_emailController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
TextField(
controller: _nameController,
decoration: InputDecoration(labelText: 'Name'),
),
TextField(
controller: _emailController,
decoration: InputDecoration(labelText: 'Email'),
),
],
);
}
}48.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.
48.2.1 Basic Pattern
class MyStatelessWidget extends StatelessWidget {
final String title;
final Color backgroundColor;
final VoidCallback? onTap;
final bool isEnabled;
const MyStatelessWidget({
Key? key,
required this.title,
this.backgroundColor = Colors.blue,
this.onTap,
this.isEnabled = true,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: isEnabled ? onTap : null,
child: Container(
padding: EdgeInsets.all(16),
color: backgroundColor,
child: Text(
title,
style: TextStyle(
color: Colors.white,
fontSize: 18,
),
),
),
);
}
}48.2.2 Using the Widget
// In your parent widget
MyStatelessWidget(
title: 'Click Me',
backgroundColor: Colors.green,
onTap: () {
print('Widget tapped!');
},
isEnabled: true,
)48.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.
48.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? key,
required this.text,
this.onPressed,
this.color = Colors.blue,
this.borderRadius = 8.0,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
style: ElevatedButton.styleFrom(
backgroundColor: color,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(borderRadius),
),
),
child: Text(text),
);
}
}User Card:
class UserCard extends StatelessWidget {
final String name;
final String email;
final String? avatarUrl;
final VoidCallback? onTap;
const UserCard({
Key? key,
required this.name,
required this.email,
this.avatarUrl,
this.onTap,
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Card(
child: ListTile(
leading: CircleAvatar(
backgroundImage: avatarUrl != null
? NetworkImage(avatarUrl!)
: null,
child: avatarUrl == null
? Icon(Icons.person)
: null,
),
title: Text(name),
subtitle: Text(email),
onTap: onTap,
),
);
}
}48.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.