Избегание Future.delayed в Flutter: лучшие альтернативы для управления временем в интерфейсе

Избегание Future.delayed в Flutter: лучшие альтернативы для управления временем в интерфейсе

FlutterPulse

Эта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀

При разработке приложений на Flutter часто приходится обрабатывать операции интерфейса, зависящие от времени, такие как прокрутка к определенному виджету после появления клавиатуры. Один из распространенных, но не рекомендуемых подходов — использование Future.delayed(). В этой статье я поделюсь несколькими лучшими альтернативами на основе реального примера из формы контактной информации.

В нашей форме контактной информации нам нужно было прокручивать к полю ввода адреса при получении фокуса, чтобы оно оставалось видимым над клавиатурой. Сначала мы использовали Future.delayed():

Проблема

В нашей форме контактной информации нам нужно было прокручивать к полю ввода адреса при получении фокуса, чтобы оно оставалось видимым над клавиатурой. Сначала мы использовали Future.delayed():

textFieldAddress2.addListener(() {
if (textFieldAddress2.hasFocus && address2Key.currentContext != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(Duration(milliseconds: 300), () {
if (address2Key.currentContext != null) {
Scrollable.ensureVisible(
address2Key.currentContext!,
alignment: 0.2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
});
});
}
});

Этот подход имеет несколько недостатков:

  • Он полагается на жестко закодированные значения времени
  • Его сложно тестировать и предсказывать
  • Он может приводить к гонкам данных
  • Он не реагирует на разные устройства и сценарии

Лучшие альтернативы

1. Использование KeyboardVisibilityController

Пакет flutter_keyboard_visibility предоставляет контроллер, который уведомляет вас о появлении или исчезновении клавиатуры:

final KeyboardVisibilityController keyboardVisibilityController = KeyboardVisibilityController();

@override
void initState() {
super.initState();

// Слушаем изменения видимости клавиатуры
keyboardVisibilityController.onChange.listen((bool visible) {
if (visible && textFieldAddress2.hasFocus && address2Key.currentContext != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(
address2Key.currentContext!,
alignment: 0.2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
}
});

// Слушатель фокуса
textFieldAddress2.addListener(() {
if (textFieldAddress2.hasFocus && keyboardVisibilityController.isVisible) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(
address2Key.currentContext!,
alignment: 0.2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
}
});
}

Этот подход реагирует на реальные события клавиатуры, а не на произвольные задержки.

2. Использование MediaQuery для обнаружения высоты клавиатуры

Flutter's MediaQuery предоставляет информацию о текущем устройстве, включая высоту клавиатуры:

@override
Widget build(BuildContext context) {
final keyboardHeight = MediaQuery.of(context).viewInsets.bottom;

if (keyboardHeight > 0 && textFieldAddress2.hasFocus && address2Key.currentContext != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(
address2Key.currentContext!,
alignment: 0.2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
});
}

return KeyboardDismissOnTap(
child: Scaffold(
// Остальная часть интерфейса
),
);
}

Этот подход автоматически срабатывает при изменении высоты клавиатуры, делая его отзывчивым к реальному состоянию интерфейса.

3. Использование AnimationController для точного управления временем

Для более точного управления временными последовательностями мы можем использовать AnimationController:

class _MyScreenState extends State<MyScreen> with SingleTickerProviderStateMixin {
late AnimationController _animationController;

@override
void initState() {
super.initState();

_animationController = AnimationController(
vsync: this,
duration: Duration(milliseconds: 300),
);

keyboardVisibilityController.onChange.listen((bool visible) {
if (visible && textFieldAddress2.hasFocus) {
_animationController.forward(from: 0.0);
}
});

_animationController.addStatusListener((status) {
if (status == AnimationStatus.completed) {
if (address2Key.currentContext != null) {
Scrollable.ensureVisible(
address2Key.currentContext!,
alignment: 0.2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
}
});
}

@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}

Этот подход дает вам точное управление последовательностью времени, оставаясь при этом связанным с реальным жизненным циклом интерфейса.

4. Использование вложенных колбэков WidgetsBinding

Для более простых случаев можно использовать вложенные вызовы WidgetsBinding.instance.addPostFrameCallback:

onPressed: () async {
final result = await Get.to(() => AddressSearchView());
if (result != null) {
postalCodeController.text = result.zonecode ?? "";
address1Controller.text = result.address ?? "";

WidgetsBinding.instance.addPostFrameCallback((_) {
textFieldAddress2.requestFocus();

WidgetsBinding.instance.addPostFrameCallback((_) {
if (address2Key.currentContext != null) {
Scrollable.ensureVisible(
address2Key.currentContext!,
alignment: 0.2,
duration: Duration(milliseconds: 300),
curve: Curves.easeInOut,
);
}
});
});
}
}

Такой подход гарантирует, что каждая операция выполняется после того, как предыдущий кадр был отрендерен, создавая последовательность операций, которые учитывают цикл обновления интерфейса.

Заключение

Хотя Future.delayed() может показаться быстрым решением для операций интерфейса, зависящих от времени, обычно лучше использовать альтернативы, которые реагируют на реальные события и состояния интерфейса. Описанные выше подходы обеспечивают более надежные, тестируемые и поддерживаемые решения для обработки временных параметров в приложениях Flutter.

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

Report Page