Flutter: Semantics控件

简介: 很久没有更新Flutter文章了,今天的文章主要是介绍一下Semantics。我们的交流QQ群:892398530。转载请标注原文出处。本来为译文,原文请戳这里这篇文章解释了Flutter中Semantics的概念。

很久没有更新Flutter文章了,今天的文章主要是介绍一下Semantics
我们的交流QQ群:892398530。
转载请标注原文出处

本来为译文,原文请戳这里
这篇文章解释了Flutter中Semantics的概念。
难度:入门级。
前言
如果你读过有关于Flutter的代码,那么你有时候你会注意到Semantics或者SemanticsConfiguration,但官方文档却对这个很有趣的话题却没有很多的资料。
这篇文章是对这个话题的介绍。与此同时也会向您展示您的应用是否会考虑使用Semantics,这取决于其重要性和兴趣度。
简而言之-这是什么东东?
官方文档Semantics类介绍如下:

一个用来描述控件树中控件含义的控件,这些描述被可访问性工具,搜索引擎或者其他其他语义分析软件使用,以确定应用程序的含义。

我个人认为这段解释云里雾里。 所以用我自己的话说就是:

言简意骇,Semantics的概念是:

  1. 完全可选(这意味着你可以完全不关心这个控件,但这并不推荐),
    2.意味着可以与Android TalkBackiOS VoiceOver一起使用(例如主要由视障人士使用),

3.意味着可以由屏幕阅读器(Screen Reader)使用,它会描述应用程序而无需查看屏幕。

通过阅读本文,我们可以意识到,如果您将应用程序定位为视障人士也可以使用,这将是多么重要...

在Flutter中他是怎么实现的?
当Flutter渲染控件树时,它还会维护第二个控件树,称为Semantics Tree,它被移动设备辅助技术(Android TalkBackiOS VoiceOver)所使用。

Semantics树的每个节点都是SemanticsNode,它可能对应于一个或一组Widgets。

每个SemanticsNode都会对应一个SemanticsConfiguration,这是一组属性,这将告诉移动设备辅助技术如何:

  1. 描述节点
  2. 与节点一起行动

SemanticsConfiguration
描述与之相关的SemanticsNode的语义信息。下面将列举其中部分属性(更详细的请查阅官方文档)。
| 名称| 描述|
| ------------- |:-------------:|
| decreasedValue | 一个执行decrease动作的返回值,如Slider |
| increasedValue | 一个执行increased动作的返回值,如Slider|
| isButton | 该节点是否是Button|
| isChecked |该节点是一种 CheckBox,是否被选中|
| isEnabled | 该节点是否可用|
| isFocused |该节点是否持有用户的焦点|
| isHeader | 该节点是否为Header |
| isSelected |该节点是否被选中 |
| isTextField |该节点是否文本字段|
| hint |在此节点上执行操作的结果的简要说明|
| label |节点描述|
| value |对值的文字性描述|

** 具有语义的隐式Flutter控件

大多数Flutter控件被隐式定义为Semantics,因为它们可能被Screen Reader引擎直接地或间接地使用。
为了解释一下这段话,下面是从Flutter源代码摘取的与Button相关代码:

class _RawMaterialButtonState extends State<RawMaterialButton> {

  ...

  @override
  Widget build(BuildContext context) {

    ...

    return new Semantics(
      container: true,
      button: true,
      enabled: widget.enabled,
      child: new ConstrainedBox(
        constraints: widget.constraints,
        child: new Material(
          ...
        ),
      ),
    );
  }
}

如何定义Semantics

有时候定义屏幕的一部分以便可以通过移动设备辅助技术进行描述可能会很有趣。
这种情况下,只需要使用下面的控件做包裹子控件的容器就可以了:

  • Semantics,当你只想描述一个特定的控件
  • MergeSemantics,当你想描述一组控件。这种情况下,被定义在该子节点下的子控件树中的不同的Semantics会被整合到一个单独的Semantics中。这对于重新组合语义非常有用,但是,如果语义冲突,结果可能是无意义的。

单一Semantics
用于定义语义的类是Semantics。 这个类有2个构造函数:一个是冗长的,一个是简洁的。
下面是定义Semantics的两种方法,解释如下:

@override
Widget build(BuildContext context){
  bool toBeMergedWithAncestors = false;
  bool allowDescendantsToAddSemantics = false;

  return new Semantics(
    container: toBeMergedWithAncestors,
    explicitChildNodes: allowDescendantsToAddSemantics,
    ...(list of all properties)...

    child: ...
  );
}

@override
Widget build(BuildContext context){
  SemanticsProperties properties = new SemanticsProperties(...);
  bool isContainer = toBeMergedWithAncestors;
  bool explicitChildNodes = allowDescendantsToAddSemantics;

  return new Semantics.fromProperties(
    container: isContainer,
    explicitChildNodes: explicitChildNodes,
    properties: properties,
    child: ...
  );
}

| 名称| 缺省值|描述|
| ------------- |:-------------:| :-------------:|
| container | false |如果值为true,则会将新的SemanticsNode添加到Semantics树中,从而不允许此Semantics与父Semantics合并。 如果值为false,则此语义将与父Semantics合并 |
| explicitChildNodes | false| 该控件的子控件是否允许将Semantics信息添加到该控件的SemanticsNode中 |

如何不使用Semantics

有时时候可能会出现根本不需要任何Semantics的情况。 这可能是屏幕的一些部分,它们只是装饰性的,对用户来说并不重要。
这种情况下,您需要使用ExcludeSemantics来去除某个控件及其子控件的Semantics。语法如下:

@override
Widget build(BuildContext context){
  bool alsoExcludeThisWidget = true;

  return new ExcludeSemantics(
    excluding: alsoExcludeThisWidget,
    child: ...
  );
}

exclude属性(默认值:true)告诉系统您是否也希望从Semantics树中排除此Widget。

如何将控件重组成一个Semantics?
在某些情况下,您可能还想重新组合一组控件的所有Semantics。
这种情况的一个基本示例可能是由Label和Checkbox组成的可视块,每个都定义了自己的Semantics。 最好的是,如果用户按下该块,则移动设备辅助技术将提供与该组相关的辅助功,而不是提供该组的每个Widget的辅助信息。

这种情况下,你应该使用MergeSemantics

注意

当你想要合并Semantics时要非常小心,因为如果你有任何冲突的Semantics,这对用户来可能说是荒谬的。 例如,如果您有一个由多个复选框组成的块,每个复选框具有不同的状态(已选中且未选中),则将检查生成的语义状态,从而误导用户。

如何调试Semantics
最后,如果要调试app中的Semantics,可以将MaterialApp的showSemanticsDebugger属性设置为true。 这将强制Flutter生成叠加层以可视化语义树。

void main(){
  runApp(new MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => new _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: new Text('My Semantics Test Application'),
      showSemanticsDebugger: true,
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new FirstScreen(),
    );
  }
}

** 结论
由于官方文档在这方面的介绍并不是很详细,我只想与您分享我的理解。

我希望这一介绍突出了这样一个事实,即如果你想有一天发布一个应用程序,考虑语义是很重要的,因为移动用户可能会打开手机的移动设备辅助技术并使用你的应用程序。 如果您的应用程序尚未准备好使用此技术,则可能存在无法使用的风险。
我希望通过本文可以让您意识到如果有一天您想发布一个app,考虑使用Semantics是很重要的,因为手机用户可能打开移动设备辅助技术并使用你的app。 如果您的app尚未准备好使用此技术,则可能存在无法使用的风险。

开心写代码~

目录
相关文章
|
10月前
|
Android开发 iOS开发 容器
Flutter控件封装之轮播图Banner
Flutter中实现轮播图的方式有很多种,比如使用三方flutter_swiper,card_swiper等等,使用这些三方,可以很快很方便的实现一个轮播图展示,基本上也能满足我们日常的开发需求,如果说,想要一些定制化的操作,那么就不得不去更改源码或者自己自定义一个,自己定义的话,Flutter中提供了原生组件PageView,可以使用它很方便的来实现一个轮播图。
193 0
|
5月前
Flutter StatefulWidget传递数据,多级控件传递数据
Flutter StatefulWidget传递数据,多级控件传递数据 在Flutter中,StatefulWidget可以通过构造函数将数据传递给其子控件,这种方式适用于一些简单的场景。但是,当存在多级嵌套控件时,将数据从祖先传递到后代可能会变得困难。在这种情况下,可以使用Flutter提供的InheritedWidget类来传递数据。
|
10月前
|
API Android开发
Flutter控件封装之视频进度条
视频控制器,三方所提供的样式,有时很难满足我们的需求,对于此情况,我们不得不在此基础上自行封装,今天所分享的文章就是一个很简单的控制器封装案例,包含了基本的播放暂停,全屏和退出全屏,以及时间和进度的展示,封装了事件回调以及各个属性的控制,基本上可以满足大部分的业务需求,即便不满足,大家也可以在此基础之上拓展。
147 0
|
10月前
|
Android开发 UED
Flutter控件之Tab选项卡封装
Tab选项卡,这是一个非常常见且权重很高的一个组件,随便打开一个App,比如掘金,如下图,首页顶部就是一个Tab选项卡,这个功能可以说,几乎每个App都会存在。
171 0
|
10月前
|
存储 Android开发
Flutter控件之图片Image封装
Flutter中偏偏原生的控件,少了很多需要又常用的属性,比如宽高,比如内外边距,又比如点击事件,如果不采取封装,视图的结构会一层嵌套一层,徒增很多的冗余代码,所以,为了简洁代码,还有为了拓展原生组件没有的属性,就不得不进行一次简单的封装,使其在调用的时候,可以很方便的实现某些功能。
|
10月前
|
API
Flutter控件之文本Text封装
文本Text比较简单,除了基类BaseWidget所提供的属性之外,又简单的扩展了部分属性,比如图文和富文本,都是系统原生的提供的,做了简单的封装。
|
10月前
|
API Android开发 容器
Flutter控件之基类Widget封装
基类的Widget主要确定以下几个方面,第一就是,自定义一个抽象类还是非抽象类,第二、继承方式,采取有状态还是无状态,第三、关于组件的点击方式,如何进行实现。
111 0
|
11月前
|
容器
Flutter的ClipRRect控件介绍
ClipRRect(Rounded Rectangle Clip)是Flutter中的一个控件,用于将其子控件剪裁为圆角矩形形状。
|
11月前
|
容器
|
11月前
|
容器
Flutter的Stack和Positioned的控件
Flutter的Stack和Positioned的控件