Какие основные виды виджетов существуют в Flutter?

Ответ

В Flutter есть две фундаментальные категории виджетов, основанные на управлении состоянием, и несколько важных специализированных типов.

1. По управлению состоянием

  • StatelessWidget: Неизменяемый виджет. Он конфигурируется один раз при создании и не меняется в течение жизненного цикла. Используется для статического или вычисляемого на основе входных параметров (final свойств) контента.

    class GreetingText extends StatelessWidget {
      final String name; // Конфигурация через конструктор
      const GreetingText({super.key, required this.name});
    
      @override
      Widget build(BuildContext context) {
        // Виджет перестраивается, только если изменились `key` или `name`
        return Text('Привет, $name!');
      }
    }
  • StatefulWidget: Виджет, который может меняться в ответ на пользовательский ввод или другие события. Он состоит из двух классов:

    1. Сам StatefulWidget (неизменяемый, как StatelessWidget).
    2. Объект State, который сохраняет mutable-состояние и содержит метод build.

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

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

    void _increment() { setState(() { // Вызов setState перестраивает виджет _count++; }); }

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

2. Специализированные виджеты

  • InheritedWidget: Эффективный механизм для передачи данных вниз по дереву виджетов без явной их передаче через конструкторы каждого виджета. Основа для Provider, Theme, MediaQuery.
  • RenderObjectWidget: Низкоуровневый виджет, который создает/обновляет RenderObject — объект, отвечающий за вычисление layout и отрисовку. SingleChildRenderObjectWidget (например, Padding) и MultiChildRenderObjectWidget (например, Row) — его потомки.
  • StatefulBuilder & LayoutBuilder: Виджеты для создания локального состояния или реагирования на изменения constraints родителя непосредственно в методе build.

Ответ 18+ 🔞

Ну вот, сидишь ты, такой весь из себя, начинаешь ковыряться во Flutter, а там, ёпта, целый зоопарк этих виджетов. И главное — все друг на друга похожи, а работают по-разному, как будто специально, чтобы голова взорвалась. Давай разберёмся без этой официальной зауми, на пальцах.

Первое и главное деление — по тому, как они с состоянием дружат. Тут всё просто, как два пальца.

Первый тип — StatelessWidget. Это как памятник, только из кода. Создали его один раз, впихнули в него какие-то финальные настройки (ну там, final String name), и всё — он застыл в вечности. Сам по себе он меняться не может. Хочешь что-то другое показать — придётся выкинуть этот виджет и создать новый с другими параметрами. Внутри у него только один метод build, который рисует виджет на основе этих самых финальных пропсов. Если name поменялось — фреймворк видит это, старый виджет на свалку, новый строит. Проще некуда.

class GreetingText extends StatelessWidget {
  final String name; // Вот эта штука — раз и навсегда. Приехали.
  const GreetingText({super.key, required this.name});

  @override
  Widget build(BuildContext context) {
    // Строится заново, только если `key` или `name` поменялись. Иначе — сидит, не дергается.
    return Text('Привет, $name!');
  }
}

Второй тип — StatefulWidget. А вот это уже хитрая жопа. Сам по себе виджет (CounterWidget) — такой же неизменяемый памятник. Но! У него есть личный раб — объект класса State. Вот этот State — он живой, в нём и хранится всё изменяемое состояние (например, счётчик _count). И когда нужно что-то поменять (пользователь кнопку тыкнул), ты вызываешь волшебный метод setState(). Он такой: «Э, бошка думай! Данные поменялись, надо перерисоваться!» — и заставляет метод build в State выполниться заново, уже с новыми цифрами.

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

  @override
  State<CounterWidget> createState() => _CounterWidgetState(); // Родил себе state-объект
}

class _CounterWidgetState extends State<CounterWidget> {
  int _count = 0; // А вот тут уже можно хранить меняющуюся хуйню

  void _increment() {
    setState(() { // Кричим фреймворку: «Ёбааать, всё поменялось, перерисуй нас!»
      _count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      onPressed: _increment,
      child: Text('Нажато $_count раз'), // И тут уже актуальная цифра
    );
  }
}

Вот и вся магия. Stateless — тупой и быстрый, Stateful — умный, но с прицепом в виде отдельного объекта.

А теперь про специалистов, без которых никуда.

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

  • RenderObjectWidget — это уже для самых отчаянных, низкоуровневая жесть. Когда обычные виджеты тебе не хватает и нужно лезть в самые дебри: управлять тем, как именно твой виджет рассчитывает свои размеры (layout) и рисуется на экране (paint). Padding или Row — они как раз внутри используют такие штуки. Без реальной необходимости туда лучше не совать нос, а то сам от себя охуеешь.

  • StatefulBuilder и LayoutBuilder — это такие хитрые костыли, но очень полезные. Допустим, тебе нужно немного изменяемого состояния прямо внутри метода build большого виджета, не плодя отдельный StatefulWidget на ровном месте — вот тебе StatefulBuilder. Или нужно сделать виджет, который реагирует на изменения размеров, которые ему выделил родитель (ну там, если экран широкий — раскладка одна, узкий — другая) — это LayoutBuilder. Удобные пацаны для специфичных задач.

Короче, вся система на этом стоит. Сначала пойми разницу между Stateless и Stateful — это основа основ. А потом уже, по мере надобности, начинай ковырять этих спецов. Главное — не пытайся всё выучить сразу, а то будет овердохуища информации, и ты просто накроешься медным тазом.