This tutorial shows you how to use ExcludeSemantics
and BlockSemantics
widgets in Flutter.
Flutter allows you to set the semantics of a widget or a subtree by wrapping it as the child of Semantics
widget. Some widgets provided by Flutter already have semantics by default. The semantics information provided by the application can be very useful for accessibility services. Therefore, we should give appropriate semantics for the user interface displayed on the screen. However, sometimes we want certain parts of the user interface to not have semantics. In this tutorial, I am going to show you how to do that.
This tutorial assumes you already understand about Semantics
widget in Flutter. If you do not, you can read our tutorial about Semantics
.
Using ExcludeSemantics
If there are some widgets that are used as decoration only and not meaningful to users, you may want to exclude the widgets from the semantics tree. To do so, you can use ExcludeSemantics
widget. The ExcludeSemantics
is a widget that drops all the semantics of its descendants.
const ExcludeSemantics({
Key? key,
bool excluding = true,
Widget? child,
})
To exclude a subtree from the semantics tree, you can set it as the child of the ExcludeSemantics
widget. The constructor also has a named argument excluding
which allows you to dynamically include or exclude a subtree from the semantics tree.
const ExcludeSemantics(
child: const Text('........'),
),
Output:
Using BlockSemantics
In some cases, you may want that the semantics are only set for particular widgets on a subtree. For example, when a popup is being displayed, it means the user can only interact with the popup. The other widgets should not have semantics at that moment. Let's say we want to create a layout like the below screenshot.
There is a button that for displaying a popup. The popup itself is created using Card
. When the popup is being displayed, the user can only interact with the popup. At that moment, the other widgets behind the popup should not have semantics, including the 'Show popup' button. Actually you can use ExcludeSemantics
to wrap the 'Show popup' button and use a state variable and set the value according to the visibility of the popup. However, if there are many widgets, you may need to use ExcludeSemantics
on each widget behind the popup. Fortunately, there is a better way to achieve the same condition. Flutter has another widget BlockSemantics
. It's a widget that drops the semantics of all widgets that were painted before it in the same semantic container.
const BlockSemantics({
Key? key,
bool blocking = true
Widget? child
})
You can use BlockSemantics
and pass the popup widget as the child
argument. The constructor has a named argument blocking
which indicates whether it should drop the semantics of other widgets painted before it. Therefore, you can pass a state variable to make the value true when the popup is being displayed or false otherwise.
class _SemanticsExampleState extends State<SemanticsExample> {
bool _showCard = false;
static const TextStyle textStyle = const TextStyle(color: Colors.white);
Widget _buildCard() {
return Card(
color: Colors.teal,
child: SizedBox(
width: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: const FlutterLogo(),
title: Text('Flutter Tutorial', style: textStyle),
subtitle: Text('by Woolha', style: textStyle),
),
ButtonTheme(
child: ButtonBar(
children: <Widget>[
TextButton(
child: const Text('OK', style: textStyle),
onPressed: () => setState(() { _showCard = false; }),
),
],
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
width: 400,
height: 160,
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
bottom: 0,
child: OutlinedButton(
child: Text('Show popup'),
onPressed: () => setState(() { _showCard = true; }),
),
),
BlockSemantics(
blocking: _showCard,
child: Visibility(
visible: _showCard,
child: _buildCard(),
),
)
],
),
),
],
),
);
}
}
When the popup is being displayed and the showSemanticsDebugger
is set to true
, the output is:
As the opposite, if we don't use BlockSemantics
(or if the blocking
value is false
), the semantics of the 'Show popup' button will not be dropped.
Full Code
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Woolha.com Flutter Tutorial',
showSemanticsDebugger: true,
home: Scaffold(
appBar: AppBar(
title: const Text('Woolha.com Flutter Tutorial'),
backgroundColor: Colors.teal,
),
body: SemanticsExample(),
),
);
}
}
class SemanticsExample extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _SemanticsExampleState ();
}
}
class _SemanticsExampleState extends State<SemanticsExample> {
bool _showCard = false;
static const TextStyle textStyle = const TextStyle(color: Colors.white);
Widget _buildCard() {
return Card(
color: Colors.teal,
child: SizedBox(
width: 200,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
const ListTile(
leading: const FlutterLogo(),
title: Text('Flutter Tutorial', style: textStyle),
subtitle: Text('by Woolha', style: textStyle),
),
ButtonTheme(
child: ButtonBar(
children: <Widget>[
TextButton(
child: const Text('OK', style: textStyle),
onPressed: () => setState(() { _showCard = false; }),
),
],
),
),
],
),
),
);
}
@override
Widget build(BuildContext context) {
return SizedBox(
width: double.infinity,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const ExcludeSemantics(
child: const Text('........'),
),
SizedBox(
width: 400,
height: 160,
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
bottom: 0,
child: OutlinedButton(
child: Text('Show Card'),
onPressed: () => setState(() { _showCard = true; }),
),
),
BlockSemantics(
blocking: _showCard,
child: Visibility(
visible: _showCard,
child: _buildCard(),
),
)
],
),
),
],
),
);
}
}
Output:
Summary
That's how to not include the semantics of widget subtrees in Flutter. You can use either ExcludeSemantics
or BlockSemantics
depending on the case.
You can also read about: