Подтверждаемое отклонение в сочетании с новой навигацией по маршруту вызывает сбой Flutter - PullRequest
2 голосов
/ 16 мая 2019

Контекст:
Я наткнулся на небольшой сбой во время тестирования ListView из Dismissible s во Флаттере. При смахивании запрещается, Dialog отображается с использованием опции confirmDismiss, для подтверждения. Это все работает хорошо, однако пользовательский интерфейс падает при тестировании маловероятного варианта использования. На странице есть несколько вариантов навигации по другим (именованным) маршрутам. Когда запрещается движение, и во время анимации выбирается опция для перехода на новый маршрут, происходит сбой.

Как воспроизвести сбой:

  1. Отклонить от делимого
  2. Во время следующей анимации (перевод положения недопустимого) нажмите на действие, которое приведет вас к новый маршрут. Временные рамки для этого минимальны, я увеличил их в примере.
  3. Новый маршрут загружается и пользовательский интерфейс останавливается

Для справки это сообщение об ошибке:

AnimationController.reverse () вызывается после AnimationController.dispose ()

Виновником является анимация, которая пытается повернуть вспять, когда она уже была уничтожена:

Пакет: трепетание / ... / виджеты / dismissible.dart: 449

Вещи, которые я пробовал:
Сначала я попытался проверить this.mounted внутри showDialog builder, но быстро понял, что проблема не в этом.
Другая идея состояла в том, чтобы обойти проблему с помощью CancelableOperation.fromFuture и затем отменить ее в методе dispose() охватывающего виджета, но это было бесполезно.

Что я могу решить или, по крайней мере, обойти эту проблему?

Код (также можно найти и клонировать здесь ):

// (...)
class _DimissibleListState extends State<DimissibleList> {
  int childSize = 3;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ListView.builder(
        itemCount: childSize,
        itemBuilder: (context, index) {
          if (index == 0) {
            return _buildNextPageAction(context);
          }
          return _buildDismissible();
        },
      ),
    );
  }

  Widget _buildNextPageAction(context) {
    return FlatButton(
      child: Text("Go to a new page"),
      onPressed: () => Navigator.of(context).pushNamed('/other'),
    );
  }

  Dismissible _buildDismissible() {
    GlobalKey key = GlobalKey();

    return Dismissible(
      key: key,
      child: ListTile(
        title: Container(
          padding: const EdgeInsets.all(8.0),
          color: Colors.red,
          child: Text("A dismissible. Nice."),
        ),
      ),
      confirmDismiss: (direction) async {
        await Future.delayed(const Duration(milliseconds: 100), () {});
        return showDialog(
          context: context,
          builder: (context) {
            return Dialog(
              child: FlatButton(
                onPressed: () => Navigator.of(context).pop(true),
                child: Text("Confirm dismiss?"),
              ),
            );
          },
        );
      },
      resizeDuration: null,
      onDismissed: (direction) => setState(() => childSize--),
    );
  }
}
...