Доступность клавиатуры в Flutter: Готова ли ваша приложение?
FlutterPulseЭта статья переведена специально для канала FlutterPulse. В этом канале вы найдёте много интересных вещей, связанных с Flutter. Не забывайте подписываться! 🚀
Овладейте системой фокусировки Flutter, чтобы создавать доступные приложения с плавной навигацией по клавиатуре для всех пользователей.
Доступность — это не функция; это право.
Даже небольшие изменения, такие как правильная навигация по клавиатуре, могут существенно повлиять на пользователей с ограниченными возможностями или особыми потребностями. Flutter предлагает средства, такие как FocusTraversalGroup, FocusTraversalOrder, ExcludeFocus и FocusNode, для навигации с помощью клавиатуры, которая является детерминированной и удобной в использовании.
В этой статье мы рассмотрим, как реализовать доступность клавиатуры с использованием этих виджетов, настроить порядок фокусировки и обеспечить инклюзивность и доступность вашего приложения для всех.
Почему важна доступность
Представьте интерфейс входа, где пользователь может просматривать интерфейс, используя только клавиатуру. Для большинства из нас это может показаться тривиальным.
Однако для человека с нарушениями подвижности, чей способ взаимодействия с компьютерной системой основан на физическом вводе с клавиатуры или вспомогательных технологиях (например, экранном чтении), логическая навигация фокусировки имеет критическое значение.
Преимущества доступности клавиатуры
- Люди с ограниченными возможностями передвижения, которые не могут пользоваться мышью или сенсорным экраном.
- Слепые или слабовидящие пользователи, которые используют индикаторы фокусировки для навигации.
- Продвинутые пользователи, которые предпочитают сочетания клавиш для более быстрого взаимодействия.
Если управление фокусировкой не управляется должным образом, пользователь может потеряться в интерфейсе, фокусировка может казаться случайной, и в худшем случае — полностью заблокироваться в недоступных частях приложения.
Flutter's focus system allows you to:
- Группировать и упорядочивать виджеты с фокусировкой.
- Исключать виджеты из дерева фокусировки.
- Настраивать способ перемещения фокусировки между виджетами.
Обзор системы фокусировки Flutter
Flutter предоставляет иерархическое дерево фокусировки, где FocusNode и связанные виджеты управляют состоянием фокусировки каждого виджета. Вот как работают основные компоненты
1. FocusNode
FocusNode — это низкоуровневый объект, который отвечает за мониторинг и управление фокусировкой одного виджета.
Use cases:
- Управление вводом с клавиатуры для TextField.
- Ручное управление фокусировкой (например, перемещение фокусировки при нажатии пользователем "Tab").
- Обнаружение, когда виджет получает или теряет фокусировку.
Example:
final FocusNode focusNode = FocusNode();
@override
Widget build(BuildContext context) {
return TextField(
focusNode: focusNode,
decoration: InputDecoration(labelText: 'Enter your name'),
);
}
2. FocusTraversalGroup
FocusTraversalGroup объединяет виджеты в область прохождения фокуса и управляет тем, как фокус перемещается между виджетами. Это обеспечивает логический и настраиваемый порядок фокусировки внутри этой группы.
Политики Для определения способа перемещения фокуса необходимо реализовать политику:
- WidgetOrderTraversalPolicy (По умолчанию). Порядок фокусировки следует иерархии дерева виджетов. Отлично подходит для большинства интерфейсов, где виджеты расположены логически.
- ReadingOrderTraversalPolicy. Следует визуальному порядку (слева направо, сверху вниз). Идеален для интерфейсов, напоминающих документ или макет формы.
- OrderedTraversalPolicy. Этот использует FocusTraversalOrder для определения пользовательской последовательности фокусировки. Наиболее адаптивен в сложных макетах, где порядок по умолчанию нелогичен.
Пример:
FocusTraversalGroup(
policy: WidgetOrderTraversalPolicy(),
child: Column(
children: [
TextField(decoration: InputDecoration(labelText: 'Username')),
TextField(decoration: InputDecoration(labelText: 'Password')),
ElevatedButton(onPressed: () {}, child: Text('Login')),
],
),
);
Здесь FocusTraversalGroup сохраняет фокус в порядке, в котором определены виджеты.
3. FocusTraversalOrder
FocusTraversalOrder настраивает последовательность фокусировки внутри FocusTraversalGroup. Это особенно актуально, если вы хотите, чтобы фокус направлялся в порядке, который не соответствует иерархии виджетов.
Существуют различные типы порядка:
- NumericFocusOrder. Фокусирует виджеты в порядке возрастания числового значения.
- LexicalFocusOrder. Фокусирует виджеты в алфавитном порядке (по порядку строк).
Пример:
FocusTraversalGroup(
policy: OrderedTraversalPolicy(), // Использует NumericFocusOrder
child: Column(
children: [
FocusTraversalOrder(
order: NumericFocusOrder(2),
child: TextField(decoration: InputDecoration(labelText: 'Password')),
),
FocusTraversalOrder(
order: NumericFocusOrder(1),
child: TextField(decoration: InputDecoration(labelText: 'Username')),
),
FocusTraversalOrder(
order: NumericFocusOrder(3),
child: ElevatedButton(onPressed: () {}, child: Text('Login')),
),
],
),
);
В этом примере порядок фокусировки будет: Username > Password > Login Button
Если FocusTraversalOrder не используется, фокус будет определяться последовательностью дерева виджетов, что может противоречить предполагаемому потоку навигации.
4. ExcludeFocus
ExcludeFocus временно "поднимает" виджет и его потомков из дерева фокусировки. Это полезно, например, для скрытия или отключения частей интерфейса без нарушения навигации с клавиатуры.
ExcludeFocus(
excluding: true,
child: ElevatedButton(onPressed: () {}, child: Text('Disabled Button')),
);
Здесь кнопка не может быть выделена, то есть фактически кнопка в традиционном смысле не фокусируема, поэтому пользователи не смогут получить к ней доступ с помощью клавиатуры.
Реальный пример: доступная форма входа
Давайте соберём все части и создадим полностью доступную страницу входа с использованием FocusTraversalGroup, FocusTraversalOrder, и FocusNode.
class AccessibleLoginForm extends StatelessWidget {
final FocusNode usernameFocus = FocusNode();
final FocusNode passwordFocus = FocusNode();
final FocusNode loginButtonFocus = FocusNode();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Accessible Login')),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
FocusTraversalOrder(
order: NumericFocusOrder(1),
child: TextField(
focusNode: usernameFocus,
decoration: InputDecoration(labelText: 'Username'),
),
),
FocusTraversalOrder(
order: NumericFocusOrder(2),
child: TextField(
focusNode: passwordFocus,
decoration: InputDecoration(labelText: 'Password'),
obscureText: true,
),
),
FocusTraversalOrder(
order: NumericFocusOrder(3),
child: ElevatedButton(
focusNode: loginButtonFocus,
onPressed: () {
// Handle login
},
child: Text('Login'),
),
),
],
),
),
),
);
}
}
Советы по тестированию доступности
Чтобы убедиться, что ваше приложение доступно:
- Проверьте с помощью клавиатуры. Можете ли вы перемещаться по всем фокусируемым виджетом?
- Проверьте индикаторы фокуса. Явно ли виден текущий фокусируемый виджет?
- Избегайте ловушек фокуса. Убедитесь, что пользователи могут выйти из скрытых или отключённых элементов.
- Проверьте с реальными пользователями. Получите обратную связь от пользователей с инвалидностью.
Заключение
Доступность является важной частью разработки приложений. С системой фокусировки Flutter, навигация по клавиатуре может быть легко реализована, чтобы сделать ваше приложение доступным и удобным для всех.
Используя FocusTraversalGroup, FocusTraversalOrder, и связанные виджеты, вы можете создать предсказуемое, интуитивно понятное поведение фокуса, которое работает для всех.