Как подавить поведение при прокрутке во Flutter?

У меня есть CustomScrollView с SliverAppBar что прячется на свитке.

На панели приложения есть кнопка поиска, при нажатии на которую появляется TextField в панель приложения.

Когда поле получает фокус, это приводит к тому, что область прокрутки прокручивается до самого верха, а панель приложения застревает в "небезопасной" области:

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

Это похоже на поведение, которое я не хочу. Я смотрел, но не мог понять механизм или как его подавить. Возможно ли это?

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

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

2 ответа

Решение

Потребовалось некоторое расследование, но в конце концов я нашел механизм такого поведения.

TextField обертывания EditableText. Когда последний получит фокус, он вызовет _showCaretOnScreen, который включает вызовrenderEditable.showOnScreen. Это пузырится и в конечном итоге вызывает поведение прокрутки.

Мы можем заставить _showCaretOnScreenвернуться сюда раньше, если мы поставимTextField взломанный ScrollController что всегда возвращается false от hasClients:

class _HackScrollController extends ScrollController {
  // Causes early return from EditableText._showCaretOnScreen, preventing focus
  // gain from making the CustomScrollView jump to the top.
  @override
  bool get hasClients => false;
}

Такое поведение не кажется преднамеренным, поэтому я сообщил об этом как об ошибке № 60422.

Предостережения

Этот обходной путь может быть не очень стабильным.

Я не знаю, какие побочные эффекты hasClients переопределение могло иметь.

В документах дляTextField сказать, что scrollControllerиспользуется "при вертикальной прокрутке ввода". В этом случае нам все равно не нужна вертикальная прокрутка, поэтому обходной путь может не вызвать никаких проблем. В моем кратком тестировании, похоже, проблем с горизонтальной прокруткой (переполнением) не возникало.

Как и в документации, попробуйте использовать параметр resizeToAvoidBottomInset со значением false (по умолчанию - true) в виджете scaffold https://api.flutter.dev/flutter/material/Scaffold/resizeToAvoidBottomInset.html

Также я бы рекомендовал создать ValueListenableBuilder после каркаса (как первый виджет в теле), чтобы не перестраивать весь каркас и только виджет тела.

Другие вопросы по тегам