Код-ревью Middle Навигация обновлено

Код-ревью: навигация после запроса

Вопрос

Обработчик кнопки «Оплатить». Найдите проблемы.

Future<void> _pay() async {
  final ok = await api.pay(widget.order);
  if (ok) {
    Navigator.of(context).push(MaterialPageRoute(
      builder: (_) => SuccessPage(order: widget.order),
    ));
  } else {
    Navigator.of(context).pushNamed('/error');
  }
}

@override
Widget build(BuildContext context) {
  if (widget.order.paid) {
    Navigator.of(context).pushReplacementNamed('/home');
  }
  return ElevatedButton(onPressed: _pay, child: const Text('Оплатить'));
}

Коротко

Навигация это побочный эффект: вызывается из обработчиков и слушателей, не из build, и после await всегда с проверкой mounted. Экран оплаты после успеха убирается из стека.

Что должен найти ревьюер

  1. Ошибкастрока 4
    После await нет проверки mounted: пользователь мог уйти, и context уже недействителен.
  2. Ошибкастрока 4
    После успешной оплаты push оставляет экран оплаты в стеке: «назад» вернёт к кнопке «Оплатить» и позволит оплатить повторно. Нужен pushReplacement или переход через роутер.
  3. Ошибкастрока 2
    Кнопку можно нажать дважды, пока идёт запрос: нужна блокировка и индикатор, иначе двойная оплата.
  4. Ошибкастрока 15
    Навигация прямо в build: побочный эффект при каждом перестроении и ошибка «setState or markNeedsBuild called during build». Перенести в слушатель состояния или redirect.
  5. Стильстрока 5
    В SuccessPage передаётся объект заказа целиком: экран не восстановится по диплинку и после перезапуска. Лучше передать id.

Разбор

Навигация это побочный эффект: вызывается из обработчиков и слушателей, не из build, и после await всегда с проверкой mounted. Экран оплаты после успеха убирается из стека. Повторное нажатие блокируется на время запроса, а сервер ещё и защищается ключом идемпотентности.

Ответьте сами, а не прочитайте

В тренажёре вопрос попадёт в повторения и вернётся, когда начнёт забываться. Плюс билеты с зачётом: 20 вопросов, не больше 2 ошибок.

Термины по теме