Ответ
Debounce (также debounceTime) — это оператор в RxDart, который откладывает эмиссию событий из потока до тех пор, пока не пройдет заданный интервал времени без новых событий. Это паттерн "подавления дребезга".
Основное применение в Flutter:
- Поиск с автодополнением
- Валидация форм
- Обработка скролла
- Отслеживание изменений размера окна
Пример поиска с debounce:
import 'package:rxdart/rxdart.dart';
class SearchBloc {
final _searchController = BehaviorSubject<String>();
// Поток с debounce 500ms
Stream<String> get searchResults => _searchController.stream
.debounceTime(Duration(milliseconds: 500))
.distinct() // Игнорируем повторяющиеся значения
.where((query) => query.length >= 3); // Минимум 3 символа
void updateQuery(String query) {
_searchController.add(query);
}
void dispose() {
_searchController.close();
}
}
// Использование в виджете:
StreamBuilder<String>(
stream: searchBloc.searchResults,
builder: (context, snapshot) {
if (snapshot.hasData) {
return _buildResults(snapshot.data!);
}
return CircularProgressIndicator();
},
)
Отличие от throttle:
- Debounce: ждет паузу, затем эмитит последнее значение
- Throttle: эмитит первое значение, затем игнорирует новые в течение интервала
Практический пример с TextField:
TextField(
onChanged: (value) {
// Без debounce: запрос на каждый символ
// С debounce: запрос только после остановки ввода на 500ms
searchBloc.updateQuery(value);
},
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: Icon(Icons.search),
),
)
Настройка:
// С кастомной логикой
stream.debounce((event) =>
TimerStream(event, Duration(milliseconds: event.length * 100))
) Ответ 18+ 🔞
А, debounce! Ну это же классика, ёпта! Представь себе: ты сидишь, печатаешь в поисковой строке "как заставить Flutter не тормозить", и с каждым нажатием клавиши у тебя летит запрос на сервер. После буквы "к" — запрос, после "ка" — ещё один, "как" — третий... Сервер уже охуел, у него волнение ебать, он думает: "Мужик, ты определись уже, что тебе надо, я не успеваю!" Вот именно от этого ада и спасает debounce.
По-простому: это такой оператор, который говорит потоку: "Слушай, дружище, не несись как угорелый. Подожди, пока пользователь перестанет тыкать в клавиши, и только тогда мы отправим последний, самый полный запрос. А все промежуточные — нахуй не нужны".
Где это втыкать, чтобы жизнь мёдом не казалась:
- Поиск с подсказками (автодополнение). Это прям его родной дом, хитрая жопа.
- Проверка формы. Чтоб не орать на пользователя "НЕПРАВИЛЬНЫЙ EMAIL!!" после каждого первого символа. Пусть допечатает, тогда и проверим.
- Скролл списка. Чтобы не пытаться подгружать новые данные, когда палец ещё не оторвался от экрана.
- Изменение размера окна. Чтобы не пересчитывать layout триста раз в секунду, когда тащат за угол.
Смотри, как это выглядит в коде, чтоб было понятно:
import 'package:rxdart/rxdart.dart';
class SearchBloc {
final _searchController = BehaviorSubject<String>();
// Вот эта строка — магия. debounceTime говорит: "Жди 500 миллисекунд тишины".
Stream<String> get searchResults => _searchController.stream
.debounceTime(Duration(milliseconds: 500)) // <--- Волшебная палочка
.distinct() // Игнорируем, если юзер ввёл "кот", стёр, и снова ввёл "кот"
.where((query) => query.length >= 3); // Ищем только осмысленные запросы
void updateQuery(String query) {
_searchController.add(query); // Сюда летит каждый символ
}
void dispose() {
_searchController.close();
}
}
// А в виджете используем как обычно:
StreamBuilder<String>(
stream: searchBloc.searchResults, // Сюда уже приходят "успокоенные" запросы
builder: (context, snapshot) {
if (snapshot.hasData) {
return _buildResults(snapshot.data!); // Строим результат
}
return CircularProgressIndicator(); // Или крутимся
},
)
Чем не throttle, спросишь? А я объясню, тут разница принципиальная, блядь.
- Debounce (наш герой): Терпеливый, как удав. Сидит, ждёт паузу. Юзер печатает "к... о... т" — тишина 500 мс — только тогда debounce выплёвывает "кот". Если юзер продолжает печатать "котэ", таймер сбрасывается, и ждём снова.
- Throttle: Скорострел. Получил первое значение "к" — сразу отправил. А потом следующие 500 мс тупо игнорирует всё, что в него летит. "о" — проигнорировано, "т" — проигнорировано. Через 500 мс, если пришло "котэ", отправит уже его.
Как прикрутить к полю ввода, чтоб работало:
TextField(
onChanged: (value) {
// Раньше тут был пиздец: запрос на КАЖДЫЙ символ. Сервер в ахуе.
// Теперь с debounce в bloc'е — запрос полетит только когда юзер остановится.
searchBloc.updateQuery(value);
},
decoration: InputDecoration(
hintText: 'Search...',
suffixIcon: Icon(Icons.search),
),
)
А если хочется извратиться? Пожалуйста, можно и кастомную логику запилить:
stream.debounce((event) =>
// Например, делаем задержку зависящей от длины строки. Длинный запрос — больше времени на обдумывание.
TimerStream(event, Duration(milliseconds: event.length * 100))
)
Короче, debounce — это как умный приятель, который держит тебя за руку и говорит: "Расслабься, чувак, не дёргайся. Подожди секунду, всё само устаканится". Без него в современном UI — просто пидарас шерстяной, доверия ебать ноль к интерфейсу.