पाठ 5 / 25

StatelessWidget and StatefulWidget

setState and the State lifecycle.

When the UI depends on changing data

A StatelessWidget depends only on its constructor arguments and inherited data, so it has just a build method. A StatefulWidget pairs an immutable widget with a separate, long-lived State object. Calling setState(() { ... }) inside the State marks it dirty so build runs again on the next frame. Key lifecycle methods: initState (one-time setup, such as creating controllers or subscriptions), didUpdateWidget (the parent passed new configuration), didChangeDependencies (an inherited widget it depends on changed), build, and dispose (release controllers, timers and subscriptions). Use the mounted property to check the State is still in the tree before calling setState after an await.

A counter with lifecycle hooks

StatefulWidget and its State.

class Counter extends StatefulWidget {
  const Counter({super.key, this.start = 0});
  final int start;

  @override
  State<Counter> createState() => _CounterState();
}

class _CounterState extends State<Counter> {
  late int _count;

  @override
  void initState() {
    super.initState();
    _count = widget.start; // read configuration via `widget`
  }

  void _increment() => setState(() => _count++);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        Text('Count: $_count'),
        IconButton(onPressed: _increment, icon: const Icon(Icons.add)),
      ],
    );
  }
}

Keep state as low as possible

Put StatefulWidgets near the leaves that actually change. Calling setState high in the tree rebuilds everything below it.

त्वरित जाँच: Where should you release a TextEditingController or StreamSubscription?

  • In build()
  • In dispose()
  • In the widget constructor
  • Nowhere; Dart frees them automatically
Answer

In dispose() — dispose is the State's cleanup hook; forgetting it leaks listeners and resources.