Что такое StreamController в Dart?

Ответ

StreamController — это класс из dart:async, который выступает в роли источника и менеджера для Stream. Он предоставляет интерфейс для добавления событий в поток (через sink) и для создания подписок на этот поток (через stream). Это основной инструмент для создания собственных потоков данных.

Ключевые компоненты:

  • stream: Геттер, возвращающий объект Stream, на который можно подписаться.
  • sink: Геттер, возвращающий объект Sink ("сток"), используемый для добавления данных или ошибок в поток.
  • add(event): Основной метод для отправки данных в поток.
  • close(): Закрывает контроллер и поток. После этого нельзя добавлять новые события.

Практический пример — создание простого таймера:

import 'dart:async';

class CountdownService {
  final StreamController<int> _controller = StreamController<int>.broadcast();
  Timer? _timer;
  int _count = 10;

  Stream<int> get countStream => _controller.stream;

  void start() {
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      if (_count > 0) {
        _controller.add(_count); // Отправляем текущее значение в поток
        _count--;
      } else {
        _controller.add(0);
        _controller.close(); // Важно закрыть контроллер и освободить ресурсы
        timer.cancel();
      }
    });
  }

  void dispose() {
    _timer?.cancel();
    _controller.close();
  }
}
// Использование во Flutter с StreamBuilder:
// StreamBuilder<int>(
//   stream: countdownService.countStream,
//   builder: (context, snapshot) => Text('${snapshot.data ?? 10}'),
// )

Типы контроллеров:

  • StreamController(): Создает одиночный поток подписки.
  • StreamController.broadcast(): Создает широковещательный поток, позволяющий множественным слушателям подписываться.

Важно: Всегда закрывайте StreamController (например, в методе dispose() у StatefulWidget), чтобы предотвратить утечки памяти.

Ответ 18+ 🔞

Ну, слушай, история про StreamController — это как будто тебе дали в руки пульт от телевизора, но не от простого, а от такого, который сам решает, что показывать. Представь: ты сидишь, смотришь в пустую коробку, а этот контроллер — он как режиссёр, который кричит: «Сейчас будет событие! Смотрите все!». И если ты не закроешь за ним дверь, он так до утра орать будет, ёпта.

Вот смотри, по-простому: StreamController из dart:async — это такой мудак-менеджер для Stream. Он и источник данных, и главный по подпискам. У него два главных рычага:

  • stream — это как динамик, в который все слушатели ушами упёрлись, ждут новостей.
  • sink — это, блядь, чёрная дыра, куда ты кидаешь свои данные, ошибки и прочую хуету, чтобы она потом вылезла из того самого динамика.

Основные кнопки на этом пульте:

  • add(event) — это когда ты берёшь и пиздячишь событие прямо в поток. Всё, понеслась.
  • close() — святое дело! Это когда ты говоришь: «Всё, мужики, концерт окончен, расходимся». Если не вызвать, будет висеть и память жрать, как последняя свинья.

Вот тебе живой пример, чтобы не быть пиздаболом. Делаем обратный отсчёт, простой таймер:

import 'dart:async';

class CountdownService {
  // Создаём самого контролёра. Broadcast — чтобы много народу могло слушать.
  final StreamController<int> _controller = StreamController<int>.broadcast();
  Timer? _timer;
  int _count = 10;

  // Это наш динамик наружу. Все будут цепляться к этому стриму.
  Stream<int> get countStream => _controller.stream;

  void start() {
    _timer = Timer.periodic(Duration(seconds: 1), (timer) {
      if (_count > 0) {
        // ВОТ ОНО! Кидаем цифру в поток. Все слушатели её тут же получат.
        _controller.add(_count);
        _count--;
      } else {
        _controller.add(0);
        _controller.close(); // ВСЁ, ПИЗДЕЦ, ЗАКРЫВАЕМ ЛАВОЧКУ! Не забываем, ядрёна вошь!
        timer.cancel();
      }
    });
  }

  // Обязательная фигня для уборки. Иначе будет течь, как решето.
  void dispose() {
    _timer?.cancel();
    _controller.close();
  }
}
// А во Flutter это юзают так, например:
// StreamBuilder<int>(
//   stream: countdownService.countStream,
//   builder: (context, snapshot) => Text('${snapshot.data ?? 10}'),
// )

И ещё важный момент, блядь, чтоб два раза не вставать. Контроллеры бывают двух видов:

  • StreamController() — обычный, одноразовый. Как личная беседа. Один подписался — второй уже не влезет.
  • StreamController.broadcast() — широковещательный, для тусовки. Как мегафон на площади — орет, и все слышат.

И главный закон, который нарушать нельзя: закрывай за собой StreamController! Вызови close() в dispose(), когда виджет сносится, или в любом другом месте, где поток больше не нужен. А то будет он висеть, как призрак, ресурсы сосать, и потом будешь охуевать, откуда утечки. Доверия к такому коду — ноль ебать. Всё, вопрос исчерпан.