← Назад к вопросам

В чем разница между Stateful и Stateless?

1.0 Junior🔥 211 комментариев
#State Management

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Разница между Stateful и Stateless виджетами

StatelessWidget и StatefulWidget — это два основных типа виджетов в Flutter, которые различаются по способности управлять и изменять своё состояние.

StatelessWidget (Без состояния)

StatelessWidget — это неизменяемый виджет, который не имеет внутреннего состояния и не может измениться во время его жизненного цикла.

// Простой StatelessWidget
class GreetingWidget extends StatelessWidget {
  final String name;
  final int age;

  const GreetingWidget({
    required this.name,
    required this.age,
  });

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text('Hello, $name! You are $age years old.'),
    );
  }
}

// Использование
GreetingWidget(name: 'Alice', age: 25)

Характеристики StatelessWidget:

  • Неизменяемый — свойства не меняются после создания
  • Быстрый — Flutter может оптимизировать рендеринг
  • Просто тестировать — поведение полностью определено параметрами
  • Нет жизненного цикла — просто build() и готово
  • Никогда не перестраивается — если параметры не изменились
// Примеры StatelessWidget из Material Design
Text('Hello')
Icon(Icons.home)
Button(onPressed: () => {})
Container()
Row(), Column()
Scaffold()

StatefulWidget (С состоянием)

StatefulWidget — это виджет, который может изменяться во время жизненного цикла. Состояние хранится в связанном классе State.

// StatefulWidget - сам виджет неизменяемый
class CounterWidget extends StatefulWidget {
  final String title;
  
  const CounterWidget({required this.title});

  @override
  State<CounterWidget> createState() => _CounterWidgetState();
}

// State - здесь живёт изменяемое состояние
class _CounterWidgetState extends State<CounterWidget> {
  int _counter = 0; // Это состояние

  void _increment() {
    setState(() {
      _counter++; // setState() уведомляет Flutter о изменении
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('${widget.title}: $_counter'),
        ElevatedButton(
          onPressed: _increment,
          child: Text('Increment'),
        ),
      ],
    );
  }
}

// Использование
CounterWidget(title: 'My Counter')

Характеристики StatefulWidget:

  • Изменяемый — может менять своё состояние
  • Сложнее — нужна связь между виджетом и State
  • Имеет жизненный цикл — initState(), build(), dispose()
  • setState() — уведомляет Flutter о изменении
  • Требует больше ресурсов — чем StatelessWidget

Жизненный цикл StatefulWidget

class _MyWidgetState extends State<MyWidget> {
  
  @override
  void initState() {
    // Вызывается один раз при создании State
    // Используй для инициализации переменных
    // Инициализация контроллеров, подписок, загрузка данных
    super.initState();
    print('initState called');
  }

  @override
  void didUpdateWidget(MyWidget oldWidget) {
    // Вызывается, когда родитель перестроил виджет с новыми параметрами
    // Сравниваются oldWidget и widget
    super.didUpdateWidget(oldWidget);
    print('didUpdateWidget called');
  }

  @override
  Widget build(BuildContext context) {
    // Вызывается при initState() и после setState()
    // Должен быть чистым - без побочных эффектов
    return Container();
  }

  @override
  void deactivate() {
    // Вызывается когда State удаляется из дерева виджетов
    print('deactivate called');
    super.deactivate();
  }

  @override
  void dispose() {
    // Вызывается в конце жизненного цикла
    // Очистка ресурсов: закрыть потоки, отписаться от подписок
    print('dispose called');
    super.dispose();
  }
}

Сравнение

АспектStatelessWidgetStatefulWidget
СостояниеНетДа
ИзменяемостьНеизменяемыйИзменяемый
ПроизводительностьБыстрееМедленнее
СложностьПростойСложнее
Жизненный циклПростойДлинный
setState()Не нуженИспользуется
ТестированиеПрощеСложнее
Использование80% приложения20% приложения

Практические примеры

Когда использовать StatelessWidget:

// Отображение статического текста
class UserCard extends StatelessWidget {
  final String name;
  final String email;

  const UserCard({
    required this.name,
    required this.email,
  });

  @override
  Widget build(BuildContext context) {
    return Card(
      child: Column(
        children: [
          Text(name),
          Text(email),
        ],
      ),
    );
  }
}

// Переиспользуемый UI компонент
class GradientButton extends StatelessWidget {
  final String label;
  final VoidCallback onPressed;

  const GradientButton({
    required this.label,
    required this.onPressed,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(colors: [Colors.blue, Colors.purple]),
      ),
      child: ElevatedButton(
        onPressed: onPressed,
        child: Text(label),
      ),
    );
  }
}

Когда использовать StatefulWidget:

// Форма с валидацией
class LoginForm extends StatefulWidget {
  @override
  State<LoginForm> createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  late TextEditingController _emailController;
  late TextEditingController _passwordController;

  @override
  void initState() {
    super.initState();
    _emailController = TextEditingController();
    _passwordController = TextEditingController();
  }

  @override
  void dispose() {
    _emailController.dispose();
    _passwordController.dispose();
    super.dispose();
  }

  void _login() {
    // Логика входа
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextField(controller: _emailController),
        TextField(controller: _passwordController),
        ElevatedButton(
          onPressed: _login,
          child: Text('Login'),
        ),
      ],
    );
  }
}

// Анимация
class RotatingBox extends StatefulWidget {
  @override
  State<RotatingBox> createState() => _RotatingBoxState();
}

class _RotatingBoxState extends State<RotatingBox> with TickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: Duration(seconds: 2),
      vsync: this,
    )..repeat();
  }

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

  @override
  Widget build(BuildContext context) {
    return RotationTransition(
      turns: _controller,
      child: Container(
        width: 100,
        height: 100,
        color: Colors.blue,
      ),
    );
  }
}

Лучшие практики

  • Используй StatelessWidget по умолчанию — он проще и быстрее
  • Переводи StatefulWidget в StatelessWidget когда больше не нужно состояние
  • Правильно очищай ресурсы в dispose() — отписывайся от подписок, закрывай потоки
  • Не передавай сложное состояние через параметры — используй Provider или Riverpod
  • Минимизируй setState() — он перестраивает весь виджет и его потомков

Правило: если виджету не нужно менять состояние — используй StatelessWidget. Это проще, быстрее и надёжнее.