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

Ответ

Это три фундаментальных типа виджетов во Flutter с разным предназначением:

  1. StatelessWidget

    • Неизменяемый (immutable). Все его поля должны быть final.
    • Не хранит собственное изменяемое состояние. Он описывает часть UI, которая зависит только от своих входных параметров (props) и контекста.
    • Создается заново при каждом изменении родительского виджета.

      class MyTitle extends StatelessWidget {
      final String text; // Параметр — final поле
      const MyTitle(this.text, {super.key});
      
      @override
      Widget build(BuildContext context) {
      return Text(text, style: Theme.of(context).textTheme.headlineMedium);
      }
      }
  2. StatefulWidget

    • Состоит из двух классов: сам неизменяемый виджет (StatefulWidget) и изменяемый объект состояния (State).
    • Хранит изменяемое состояние (данные, которые могут меняться в течение жизни виджета).
    • Перестраивает UI при вызове setState() в его объекте State.

      
      class Counter extends StatefulWidget {
      const Counter({super.key});
      
      @override
      State<Counter> createState() => _CounterState();
      }

    class _CounterState extends State { int _count = 0; // Изменяемое состояние

    void _increment() { setState(() { // Триггер перерисовки _count++; }); }

    @override Widget build(BuildContext context) { return ElevatedButton( onPressed: _increment, child: Text('Нажато $_count раз'), ); } }

  3. InheritedWidget

    • Специальный виджет для эффективного распространения данных вниз по дереву.
    • Позволяет любому потомку в поддереве получить доступ к данным без их явной передачи через конструкторы каждого промежуточного виджета (решает "проблему проваливания пропсов").
    • Ключевой механизм для таких сущностей, как Theme, MediaQuery, а также для state-менеджмента в пакетах вроде Provider.
      // Потомок получает данные через статический метод .of(context)
      final themeData = Theme.of(context);
      final screenSize = MediaQuery.of(context).size;

Итог: Stateless и Stateful — строительные блоки UI. InheritedWidget — это инфраструктурный виджет для передачи данных и управления зависимостями.

Ответ 18+ 🔞

Э, сабака сука, слушай сюда, я тебе сейчас про три кита Flutter'а расскажу, чтобы ты не ходил потом как манда с ушами. Это же ёперный театр, как люди путаются!

Первый тип — StatelessWidget. Это как твой старый дед, который уже нихуя не меняется. Он родился с такими параметрами — такими и помрёт. Все поля у него final, то есть конченые. Он просто рисует интерфейс на основе того, что ему скормили сверху, и всё. Родитель поменял данные — этого деда выкинули нахуй и создали нового, с новыми данными. Никакой своей памяти, никакого состояния. Просто рисует и не парится.

class MyTitle extends StatelessWidget {
  final String text; // Вот видишь? final. Принял и зафиксировал.
  const MyTitle(this.text, {super.key});

  @override
  Widget build(BuildContext context) {
    return Text(text, style: Theme.of(context).textTheme.headlineMedium);
  }
}

Второй — StatefulWidget. А вот это уже хитрая жопа. Он состоит из двух половинок, как полупидор какой-то. Первая половинка — сам виджет — тоже вроде как неизменяемая обёртка. Но внутри у него живёт вторая половинка — объект State (состояние). И вот эта штука — она помнит всё! Нажал кнопку — счётчик увеличился, вызвал setState() — и виджет перерисовался, но объект состояния-то остался жить! Он хранит эти изменяемые данные. Без него интерактивность — пизда рулю.

class Counter extends StatefulWidget {
  const Counter({super.key});

  @override
  State<Counter> createState() => _CounterState(); // Родил состояние
}

class _CounterState extends State<Counter> {
  int _count = 0; // А вот оно, изменяемое состояние! Может расти.

  void _increment() {
    setState(() { // Магический пинок для перерисовки
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _increment,
      child: Text('Нажато $_count раз'), // Смотри, данные из состояния!
    );
  }
}

Третий — InheritedWidget. Это вообще отдельная песня, ядрёна вошь. Представь, ты на самом верху дерева виджетов, и тебе нужно передать, например, тему вниз, глубоко-глубоко. Ты что, будешь прокидывать её через конструкторы двадцати промежуточных виджетов, которые сами по себе в ней не нужны? Это же пиздопроебибна! Вот для этого и есть InheritedWidget. Он вешает данные на дерево, а любой его потомок может до них дотянуться ручкой, через context, без всей этой ебалы с передачей. Theme.of(context) или MediaQuery.of(context) — это всё он, красавец, работает.

// Сидишь где-то в глубине, и тебе вдруг понадобилась тема.
// Не надо ничего прокидывать! Просто:
final themeData = Theme.of(context); // Взял и вытащил. Волшебство, блядь.
final screenSize = MediaQuery.of(context).size;

Итог, ёпта: Stateless и Stateful — это кирпичи, из которых стены лепишь. А InheritedWidget — это скрытая проводка и система вентиляции, которую ты раскидываешь по всему дому, чтобы потом в любой комнате мог включить свет, не таская с собой генератор. Понял теперь, чувак?