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? 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'),
            ),
          ],
        ),
      ),
    );
  }
}

41.1.2 Using the Widget

// In your parent widget
MyStatefulWidget(
  title: 'Counter App',
  initialCount: 5,
  onPressed: () {
    print('Button was pressed!');
  },
)

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? 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'),
        ),
      ],
    );
  }
}

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? 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,
          ),
        ),
      ),
    );
  }
}

41.2.2 Using the Widget

// In your parent widget
MyStatelessWidget(
  title: 'Click Me',
  backgroundColor: Colors.green,
  onTap: () {
    print('Widget tapped!');
  },
  isEnabled: true,
)

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? 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,
      ),
    );
  }
}

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.