Ответ
Subject — это специальный контроллер потока из библиотеки rxdart, который одновременно является и Stream (на него можно подписаться), и Sink (в него можно добавлять события). Это «горячий» поток, который начинает эмитировать данные сразу и может иметь несколько подписчиков.
Основные типы Subject в rxdart:
PublishSubject: Передает только события, произошедшие после подписки. Не хранит предыдущие значения.BehaviorSubject: Кеширует и передает новым подписчикам последнее отправленное значение.ReplaySubject: Кеширует и передает новым подписчикам заданное количество последних значений.
Пример использования BehaviorSubject для управления состоянием:
import 'package:rxdart/rxdart.dart';
class AuthService {
// Создаем Subject, который хранит последнее значение (начальное — false)
final _isLoggedIn = BehaviorSubject<bool>.seeded(false);
// Предоставляем поток для подписки в UI
Stream<bool> get isLoggedInStream => _isLoggedIn.stream;
void login() {
// ... логика входа
_isLoggedIn.add(true); // Уведомляем всех подписчиков
}
void logout() {
// ... логика выхода
_isLoggedIn.add(false);
}
void dispose() {
_isLoggedIn.close(); // Важно закрыть при завершении
}
}
// В UI:
StreamBuilder<bool>(
stream: authService.isLoggedInStream,
builder: (context, snapshot) {
return snapshot.data == true ? HomePage() : LoginPage();
},
)
Зачем использовать? Subject идеально подходит для ручного управления событиями в реактивном стиле, например, для стримов событий из UI (клики, текстовый ввод) или как основа для простых State Management решений.
Ответ 18+ 🔞
Давай разберём эту штуку, а то смотрю — народ путается, как слепой кот в подвале. Subject из rxdart — это такая универсальная палка-копалка, которая умеет и слушать, и говорить. Представь рацию: ты можешь в неё орать (это Sink), а другие могут эту речь ловить (это Stream). И самое главное — она «горячая», то есть вещает сразу, даже если никто не слушает, как сосед за стенкой в три ночи.
Какие бывают эти самые Subject, чтобы не путать их с чем попало:
PublishSubject— как сплетня в очереди: передаёт только то, что услышал после того, как подошёл. Что было до — не в курсе, не хранит.BehaviorSubject— уже хитрая жопа: помнит последнюю переданную фигню и сразу плюёт её новому подписчику в ухо. Типа «на, держи, чувак, актуальное».ReplaySubject— вообще маньяк-архивариус: может запомнить овердохуища последних значений и вывалить их всем новеньким. Настраивается, конечно, но суть — помнит всё, как слон.
Вот тебе живой пример на BehaviorSubject, чтобы вообще ни хуя не осталось непонятного:
import 'package:rxdart/rxdart.dart';
class AuthService {
// Создаём Subject, который изначально знает, что мы не залогинены (seeded(false))
final _isLoggedIn = BehaviorSubject<bool>.seeded(false);
// Даём наружу только поток для чтения, чтобы какой-нибудь распиздяй не накрутил туда левых данных
Stream<bool> get isLoggedInStream => _isLoggedIn.stream;
void login() {
// ... там твоя логика входа, запросы на сервер и прочая муть
_isLoggedIn.add(true); // БАМ! Кидаем всем подписчикам true. Все узнают, что зашли.
}
void logout() {
// ... выходим, чистим куки, грустим
_isLoggedIn.add(false); // И опять уведомляем мир. Реактивно, блядь!
}
// А это святое — не забыть закрыть кран, а то потечёт
void dispose() {
_isLoggedIn.close();
}
}
// А в UI прикручиваем это дело через StreamBuilder, например:
StreamBuilder<bool>(
stream: authService.isLoggedInStream,
builder: (context, snapshot) {
// snapshot.data — это наш булевский флаг
return snapshot.data == true ? HomePage() : LoginPage();
},
)
Зачем это всё, спросишь? Да затем, ёпта! Subject — это же ебать какая удобная штука, когда нужно вручную управлять потоком событий. Кликнули кнопку — subject.add(событие). Ввели текст — ещё add. Сделали из этого простенький state management для мелких задач — вообще красота. Главное — не забывай dispose(), а то будет тебе хиросима с утечками памяти.