Программа чтения с экрана - полезная функция для пользователей с плохим зрением или тех, кто не может легко пользоваться сенсорным экраном. Таким пользователям приходится полагаться на звуковые предупреждения и уведомления для навигации по приложению.

Одной из лучших особенностей семантических виджетов Flutter является то, что они позволяют разработчикам создавать приложения, ориентированные на пользователей с различными типами визуальных, возрастных или физических ограничений. Это повышает удобство использования и доступность вашего приложения, позволяя таким пользователям быть более продуктивными и независимыми. В этом блоге мы поймем, как работают эти виджеты, создав программу чтения с экрана, которая направляет пользователей звуковыми подсказками.

Как семантические виджеты работают во Flutter

Виджет «Семантика» аннотирует дерево виджетов с контуром своего дочернего элемента. Итак, после того, как вы захотите создать собственное описание для пары виджетов, вы сможете обернуть его виджетом семантики. Для конечного пользователя это означает, что он или она могут быть осведомлены о том, какое действие будет выполнено при нажатии на экранный объект, такой как кнопка.

Начнем с тестирования семантического конструктора.

Если вы хотите сформировать константу объекта Semantics, выберите Semantics в конструкторе свойств. По умолчанию эти свойства равны нулю. Давайте рассмотрим это на простом примере:

FaceImage (file:’$name.png’)

Когда пользователь выбирает Изображение или Значок в режиме двусторонней связи, по умолчанию он произносит только звук «Изображение». Чтобы иметь собственный звук или сообщение, оберните виджет виджетом Semantics и добавьте свойство label:

Semantics ( FaceImage(file:’$name.png’, ), label: ‘An image of $name’ )

На изображении выше предположим, что мы решили произносить «$ name» как «Bird». В этом случае во время разговора / закадрового голоса контур будет читаться как «Изображение птицы».

Все просто, правда?

В этом волшебство использования семантики. Если мы установим для excludeSemantics значение true, метка по умолчанию не будет считана. В этом сценарии примененное описание метки работает идеально.

Кроме того, если вы добавили в виджет GestureDetector, это не сработает. Вместо этого нужно добавить свойство OnTap в семантику и применить там идентичную логику, а также свойство OnTap для GestureDetector.

Чтобы лучше понять, давайте рассмотрим простой пример: если я нажму кнопку, по умолчанию двусторонняя связь / закадровый голос будут читаться как «Дважды нажмите для активации». Благодаря этому вы сможете исключить семантику, чтобы исключить инструкцию по умолчанию. В ярлыке вы должны включить «Нажмите, чтобы выбрать», поскольку мы исключили предложение «Нажмите дважды, чтобы активировать».

Есть 3 различных категории семантических виджетов

  1. Объединить семантику: он объединяет всю доступную дочернюю семантику в одну, и поэтому программа чтения с экрана обработает их все без каких-либо задержек.
  2. Исключить семантику: исключает поддерево из дерева семантики, которое является избыточным и не так важно для пользователя.
  3. Индексированная семантика: помогает отслеживать релевантную информацию, передаваемую в программы чтения с экрана.
ListView.builder(
    itemCount: 5,
    addSemanticIndexes: false,
    semanticChildCount: 3,
    itemBuilder: (context,position){
      return MergeSemantics(
        child: Semantics(label: 'Container with size 200 and red background',
          enabled: position == 1 ? true : false,
          selected: position == 0 ? true : false,
          child: Container(color: Colors.red, height: 200, width: 200,
            child: Column(
              children: [
                Text('First container text is item $position'),
                BlockSemantics(child:Text('Second container text is item $position')),
                ExcludeSemantics(child:Text('Third container text is item $position')),
                Text('Fourth container text is item $position'),
              ],),),),);
    }
 );

›› MergeSemantics

В приведенном выше примере MergeSemantics добавляется в качестве корневого виджета к виджету семантики. Следовательно, он объединит всю дочернюю семантику в одну, и программа чтения с экрана прочитает все сразу. Обратите внимание, что он будет считывать семантическую метку, независимо от того, включена она или нет, и выбрана она или нет. Он также зачитает текстовый виджет.

Итак, мы создали представление списка с 5 элементами, оно отключит все элементы, кроме второго. Это потому, что мы применили свойство enabled на основе позиции. То же самое применимо к выбранному свойству, оно установит выбранное состояние для основного элемента.

›› ExcludeSemantics и BlockSemantics

В приведенных выше примерах мы использовали BlockSemantics для второго дочернего элемента в контейнере. Следовательно, виджеты перед этим узлом опускаются и не читаются программами чтения с экрана. Если вы предпочитаете исключить какой-либо элемент из контейнера, вы должны использовать ExcludeSemantics, аналогично тому, как он был применен к третьему элементу контейнера в приведенном выше примере.

Давайте запустим приложение и щелкнем по основному элементу. Здесь программа чтения с экрана должна сказать: «Контейнер размером 200 с красным фоном, второй текст контейнера - это элемент 0, четвертый текст контейнера - это элемент 0».

›› Индексированная семантика

Индексированная семантика используется для считывания релевантной информации в прокручиваемом виджете, таком как Listview (как показано выше).

Разделители в ListView, Talkback / voice-over не будут считывать разделитель. Кроме того, описание элемента, которое будет считываться следующим, также можно указать с помощью свойства index.

ListView(semanticChildCount: 2, 
  children: const <Widget>[
    IndexedSemantics(index: 1, child: Text('First')),Spacer(),
    IndexedSemantics(index: 0, child: Text('Second')),Spacer(),
],);

Давайте поработаем с примером с использованием Semantic.fromProperties, чтобы лучше понять его.

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

ListView.builder(
    itemCount: 5,
    addSemanticIndexes: false,
    semanticChildCount: 3,
    itemBuilder: (context,position){
   final String semanticsLabel = 'Container with size 200 and red background';
   final String enabled: position == 1 ? true : false,
   final String selected: position == 0 ? true : false,
    final SemanticsProperties properties = SemanticsProperties(label:semanticsLabel,enabled:enabled,selected:selected);
      return MergeSemantics(
        child:Semantics.fromProperties(properties: properties, excludeSemantics: true, child: 
         child: Container(color: Colors.red, height: 200, width: 200,
            child: Column(
              children: [
                Text('First container text is item $position'),
                BlockSemantics(child:Text('Second container text is item $position')),
                ExcludeSemantics(child:Text('Third container text is item $position')),
                Text('Fourth container text is item $position'),
              ],),),),);
    }
 );

Надеюсь, что приведенные выше примеры и понимание различных типов семантических виджетов во Flutter помогут вам создавать удобные и доступные мобильные приложения. Спасибо за чтение!

Flutter является зарегистрированным товарным знаком Google LLC.

Автор - Прадип Кумар, DLT Labs

Об авторе: Pradeep - опытный разработчик мобильных приложений, имеющий опыт создания успешных приложений для Android, iOS и Flutter, которые были хорошо приняты и коммерчески жизнеспособны. .

Отказ от ответственности: эта статья изначально была опубликована на странице блога DLT Labs:
https://www.dltlabs.com/blog/make-apps-more-accessible-with-flutters-semantic-widgets-224385