В чем разница между StatefulWidget и StatelessWidget в Flutter?

«В чем разница между StatefulWidget и StatelessWidget в Flutter?» — вопрос из категории Flutter виджеты, который задают на 29% собеседований Flutter Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

В разработке Flutter-приложений я постоянно работаю с обоими типами виджетов, и понимание их различий критично для производительности приложения.

StatelessWidget — неизменяемый виджет, который зависит только от своих параметров (final полей). Я использую его для представления статического контента:

// Пример из моего кода: отображение информации о пользователе
class UserProfile extends StatelessWidget {
  final String userName;
  final int userAge;
  final String avatarUrl;

  const UserProfile({
    required this.userName,
    required this.userAge,
    required this.avatarUrl,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        CircleAvatar(backgroundImage: NetworkImage(avatarUrl)),
        Text('Имя: $userName'),
        Text('Возраст: $userAge'),
      ],
    );
  }
}

StatefulWidget состоит из двух классов и хранит изменяемое состояние. Я использую его для интерактивных элементов:

// Пример: счетчик с анимацией
class AnimatedCounter extends StatefulWidget {
  final Duration animationDuration;

  const AnimatedCounter({required this.animationDuration});

  @override
  _AnimatedCounterState createState() => _AnimatedCounterState();
}

class _AnimatedCounterState extends State<AnimatedCounter> 
    with SingleTickerProviderStateMixin {
  int _count = 0;
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: widget.animationDuration,
      vsync: this,
    );
  }

  void _increment() {
    setState(() {
      _count++;
      _controller.forward(from: 0);
    });
  }

  @override
  Widget build(BuildContext context) {
    return ScaleTransition(
      scale: _controller,
      child: ElevatedButton(
        onPressed: _increment,
        child: Text('Счет: $_count'),
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

Ключевые различия из моего опыта:

  1. Производительность: StatelessWidget легче и быстрее, так как не имеет состояния и lifecycle методов.
  2. Иммутабельность: StatelessWidget всегда immutable, что упрощает отладку и тестирование.
  3. Использование:
    • Stateless: для отображения данных, иконок, текста
    • Stateful: для форм, анимаций, таймеров, всего что меняется со временем
  4. Жизненный цикл: StatefulWidget имеет initState(), dispose(), didUpdateWidget() методы.

В современных приложениях я стараюсь минимизировать использование StatefulWidget, вынося состояние в провайдеры (Provider, Riverpod), что делает виджеты более переиспользуемыми и тестируемыми.