天天看點

Flutter: Semantics控件

很久沒有更新Flutter文章了,今天的文章主要是介紹一下Semantics。

我們的交流QQ群:892398530。

轉載請标注

原文出處

本來為譯文,原文

請戳這裡

這篇文章解釋了Flutter中Semantics的概念。

難度:入門級。

前言

如果你讀過有關于Flutter的代碼,那麼你有時候你會注意到Semantics或者SemanticsConfiguration,但官方文檔卻對這個很有趣的話題卻沒有很多的資料。

這篇文章是對這個話題的介紹。與此同時也會向您展示您的應用是否會考慮使用Semantics,這取決于其重要性和興趣度。

簡而言之-這是什麼東東?

官方文檔 對Semantics類介紹如下:

一個用來描述控件樹中控件含義的控件,這些描述被可通路性工具,搜尋引擎或者其他其他語義分析軟體使用,以确定應用程式的含義。

我個人認為這段解釋雲裡霧裡。 是以用我自己的話說就是:

言簡意駭,Semantics的概念是:
  1. 完全可選(這意味着你可以完全不關心這個控件,但這并不推薦),

    2.意味着可以與Android TalkBack或iOS VoiceOver一起使用(例如主要由視障人士使用),

3.意味着可以由螢幕閱讀器(Screen Reader)使用,它會描述應用程式而無需檢視螢幕。

通過閱讀本文,我們可以意識到,如果您将應用程式定位為視障人士也可以使用,這将是多麼重要...

在Flutter中他是怎麼實作的?

當Flutter渲染控件樹時,它還會維護第二個控件樹,稱為Semantics Tree,它被移動裝置輔助技術(Android TalkBack或iOS 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尚未準備好使用此技術,則可能存在無法使用的風險。

開心寫代碼~

繼續閱讀