Всем привет, меня зовут Дмитрий Андриянов.
я Flutter-разработчик В Серфинг .
Для создания эффективного и продуктивного пользовательского интерфейса достаточно базовой библиотеки Flutter. Но бывают случаи, когда нужно реализовать конкретные случаи, и тогда приходится копать глубже.
Вводный
Есть экран с множеством текстовых полей.Их может быть как 5, так и 30. Между ними могут быть различные виджеты.
Задача
- Разместите блок с кнопкой «Далее» над клавиатурой для перехода к следующему полю.
- При смене фокуса прокрутите поле до блока кнопкой «Далее».
Проблема
Блок с кнопкой перекрывает текстовое поле.Необходимо реализовать автоматическую прокрутку до размера перекрываемого пространства текстового поля.
Подготовка к решению
1. Возьмем экран из 20 полей.Код:
При фокусе в текстовом поле мы видим следующую картину:List<String> list = List.generate(20, (index) => index.toString()); @override Widget build(BuildContext context) { return Scaffold( body: SingleChildScrollView( child: SafeArea( child: Padding( padding: const EdgeInsets.all(20), child: Column( children: <Widget>[ for (String value in list) TextField( decoration: InputDecoration(labelText: value), ) ], ), ), ), ), ); }
Поле хорошо видно и все в порядке.
2. Добавьте блок с кнопкой.
Для отображения блока используется Наложение .
Это позволяет отображать панель независимо от виджетов на экране и не использовать обертки стека.
При этом прямого взаимодействия между полями и блоком «Далее» у нас нет. хороший статья о наложении.
Вкратце: Overlay позволяет накладывать виджеты поверх других виджетов с помощью куча наложения Оверлейная запись позволяют вам управлять соответствующим наложением.
Код: bool _isShow = false;
OverlayEntry _overlayEntry;
KeyboardListener _keyboardListener;
@override
void initState() {
SchedulerBinding.instance.addPostFrameCallback((_) {
_overlayEntry = OverlayEntry(builder: _buildOverlay);
Overlay.of(context).
insert(_overlayEntry); _keyboardListener = KeyboardListener() .
addListener(onChange: _keyboardHandle); }); super.initState(); } @override void dispose() { _keyboardListener.dispose(); _overlayEntry.remove(); super.dispose(); } Widget _buildOverlay(BuildContext context) { return Stack( children: <Widget>[ Positioned( bottom: MediaQuery.of(context).
viewInsets.bottom, left: 0, right: 0, child: AnimatedOpacity( duration: const Duration(milliseconds: 200), opacity: _isShow ? 1.0 : 0.0, child: NextBlock( onPressed: () {}, isShow: _isShow, ), ), ), ], ); void _keyboardHandle(bool isVisible) { _isShow = isVisible; _overlayEntry?.
markNeedsBuild();
}
3. Как и ожидалось, блок перекрывает поле.
Идеи решения
1. Возьмите текущую позицию прокрутки экрана из ScrollController и прокрутите до поля.Размеры поля неизвестны, особенно если оно многострочное, то прокрутка до него даст неточный результат. Решение не будет идеальным или гибким.
2. Добавьте размеры виджетов за пределы списка и учтите прокрутку.
Если вы установите для виджетов фиксированную высоту, то, зная положение прокрутки и размер виджетов, вы будете знать, что сейчас отображается и как далеко вам нужно прокрутить, чтобы отобразить определенный виджет. Минусы :
- Вам придется учитывать все виджеты вне списка и задавать им фиксированные размеры, которые будут использоваться в расчетах, что не всегда соответствует требуемому дизайну и поведению интерфейса.
- Изменения в пользовательском интерфейсе приведут к изменениям в расчетах.
Минус - Из коробки такой опции нет. 4. Используйте слой рендеринга.
Основанный на статьи , Flutter умеет расположить своих дочерних элементов в дереве, а значит эту информацию можно вытащить.
Ответственный за рендеринг РендерОбъект , то мы отправимся к нему.
РендерБокс имеет поле размера с шириной и высотой виджета.
Они рассчитываются при рендеринге для виджетов: будь то списки, контейнеры, текстовые поля (даже многострочные) и т.д.
Вы можете получить RenderBox через context context.findRenderObject() as RenderBox
Вы можете использовать GlobalKey для получения контекста поля.
Минус : GlobalKey — не самая простая вещь.
И лучше использовать его как можно реже.
«Виджеты с глобальными ключами перерисовывают свои поддеревья при переходе из одного места дерева в другое.
Чтобы перерисовать свое поддерево, виджет должен переместиться на новое место в дереве в том же кадре анимации, в котором он был удален из старого местоположения.
Глобальные ключи относительно дороги с точки зрения производительности.
Если вам не нужны какие-либо из перечисленных выше функций, рассмотрите возможность использования Key, ValueKey, ObjectKey или UniqueKey. Вы не можете одновременно включить в дерево два виджета с одним и тем же глобальным ключом.
Если вы попытаетесь это сделать, произойдет ошибка выполнения».
Источник .
На самом деле, если держать 20 GlobalKey на экране, ничего страшного не произойдет, но поскольку использовать его рекомендуется только при необходимости, попробуем поискать другой путь.
Решение без GlobalKey
Мы будем использовать слой рендеринга.Первое, что нам нужно проверить — сможем ли мы что-то вытащить из RenderBox и те ли это данные, которые нам нужны.
Код для проверки гипотезы: FocusNode get focus => widget.focus;
@override
void initState() {
super.initState();
Future.delayed(const Duration(seconds: 1)).
then((_) {
Теги: #Разработка мобильных приложений #программирование #flutter #dart #surf
-
Тендеры И Вороватый
19 Oct, 24 -
Фотообзор Hp Proliant Dl980 G7
19 Oct, 24 -
Как Трассировка Стека Работает На Arm
19 Oct, 24