Во
Flutter, как и в любом другом фреймворке, периодически возникает задача сделать задержку в выполнении кода. На первый взгляд кажется, что можно просто взять и приостановить выполнение на пару секунд. Но если сделать это напрямую, интерфейс просто перестанет реагировать на касания.
Давайте разберемся почему так происходит и как делать задержки правильно.
Почему нельзя блокировать основной поток:
В мобильных приложениях есть главный поток, который отвечает за отрисовку интерфейса и обработку жестов. Если этот поток занят каким-то длительным синхронным действием, например циклом с задержкой, то интерфейс перестает обновляться до тех пор, пока операция не завершится.
На андроиде если заблокировать главный поток больше чем на 5 секунд, система вообще предложит пользователю закрыть приложение. На
iOS такого жесткого порога нет, но интерфейс все равно перестает реагировать, что мгновенно портит пользовательский опыт.
Поэтому все, что может занять время, нужно выполнять асинхронно, чтобы основной поток оставался свободным для отрисовки и обработки ввода.
Как правильно сделать задержку во Flutter:
Во
Flutter есть несколько способов сделать асинхронную задержку. Самый простой и правильный - использовать
Future.delayed():
Future.delayed(Duration(seconds: 2), () {
// код, который выполнится через 2 секунды
});
Этот метод возвращает
Future, который завершится через указанное время. Все это время основной поток продолжает работать, интерфейс остается отзывчивым, а через 2 секунды выполняется переданный код.
Если нужно подождать внутри асинхронной функции, используется
await:
void doSomething() async {
await Future.delayed(Duration(seconds: 2));
// код после задержки
}
Когда еще может понадобиться задержка:
Иногда задержка нужна не только для отложенного выполнения кода, но и для создания анимаций с задержкой, или чтобы дать пользователю время увидеть изменения на экране перед следующим действием.
Например после того как пользователь что-то отправил, можно показать иконку успеха, а через полторы секунды автоматически закрыть экран. Или сделать паузу между шагами в обучающем процессе.
Во всех этих случаях
Future.delayed() - оптимальный выбор.
Чего делать не стоит:
Самый простой и опасный способ сделать задержку - использовать
sleep(). Это синхронная блокировка текущего потока на указанное время. Если вызвать ее на главном потоке - интерфейс полностью перестанет реагировать на касания.
Также не стоит пытаться эмулировать задержку через циклы с проверкой времени. Это не только блокирует
UI, но и бессмысленно грузит процессор.
Если нужно больше контроля - Timer:
В некоторых сценариях
Future.delayed() не подходит. Например, если нужно выполнять действие периодически, отменять его или запускать несколько раз с интервалом.
Для этого во
Flutter есть класс
Timer:
Timer(Duration(seconds: 2), () {
// выполнится один раз через 2 секунды
});
И для периодического выполнения:
Timer.periodic(Duration(seconds: 2), (timer) {
// будет выполняться каждые 2 секунды
});
Timer работает асинхронно и не блокирует
UI. Его легко отменить, вызвав
timer.cancel().
Когда лучше не использовать задержку:
Иногда задержку добавляют на всякий случай: подождать, пока подгрузится что-то, или дать время на анимацию. В таких случаях лучше использовать механизмы, которые реагируют на фактические события, а не на таймер.
Вместо задержки можно дождаться завершения асинхронной операции и уже после этого переходить к следующему шагу. Вместо таймера для анимации - использовать
AnimatedBuilder или другие анимационные виджеты.
Если задержка используется для решения проблем с синхронизацией, это верный признак, что что-то в архитектуре пошло не так. Стоит пересмотреть подход, а не маскировать проблему таймаутами.
Вывод:
Для создания задержек во
Flutter используйте асинхронные методы
Future.delayed() или
Timer. Они не блокируют основной поток, интерфейс остается отзывчивым, а пользователь не видит зависаний.
Использование синхронных задержек на главном потоке почти всегда приводят к ошибкам. В мобильной разработке зависший интерфейс для пользователя равноценен сломанному приложению. Даже если внутри все работает, внешне он этого не видит.
Если задержка используется для ожидания завершения асинхронной операции, лучше дождаться ее реального завершения через
await или
then(), а не полагаться на фиксированный таймаут.