Tue, Mar 14, 2023
Read in 4 minutes
In this post we learn how to use FocusableActionDetector widget in flutter.
FocusableActionDetector
is a Flutter widget that allows you to detect and handle focus and action events within a widget tree. It is commonly used in building accessible user interfaces where users can navigate through different widgets using keyboard, screen readers, or other input methods. In this tutorial, we will explore how to use FocusableActionDetector
in detail with code examples.
The simplest way to use FocusableActionDetector
is to wrap it around a widget that you want to make focusable and actionable. Here’s an example:
FocusableActionDetector(
actions: {
ActivateAction.key: CallbackAction(
onInvoke: () {
// Do something when the widget is activated
},
),
},
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
In this example, we have wrapped a Container
widget with FocusableActionDetector
. We have also defined an actions
map that maps a key to a CallbackAction
. The ActivateAction.key
is a built-in key that represents the “activate” action. When the user activates the widget (e.g. by pressing the Enter key or clicking on it), the CallbackAction
will be invoked and we can do something inside its onInvoke
callback.
In addition to the “activate” action, FocusableActionDetector
allows you to specify focus traversal keys that enable users to move the focus to other widgets using keyboard shortcuts. Here’s an example:
FocusableActionDetector(
focusTraversalKeys: {
// Move the focus to the next widget when the user presses the Tab key
LogicalKeyboardKey.tab: TraversalDirectionalAction.increment,
// Move the focus to the previous widget when the user presses the Shift + Tab keys
LogicalKeyboardKey.shiftLeft: TraversalDirectionalAction.decrement,
},
actions: {
ActivateAction.key: CallbackAction(
onInvoke: () {
// Do something when the widget is activated
},
),
},
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
In this example, we have added two focus traversal keys to the focusTraversalKeys
map: LogicalKeyboardKey.tab
and LogicalKeyboardKey.shiftLeft
. The former moves the focus to the next widget in the focus order, while the latter moves the focus to the previous widget.
FocusableActionDetector
also allows you to handle focus events, such as when the widget gains or loses focus. Here’s an example:
FocusableActionDetector(
onFocusChange: (hasFocus) {
if (hasFocus) {
// Do something when the widget gains focus
} else {
// Do something when the widget loses focus
}
},
actions: {
ActivateAction.key: CallbackAction(
onInvoke: () {
// Do something when the widget is activated
},
),
},
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
In this example, we have defined an onFocusChange
callback that is called when the widget gains or loses focus. Inside the callback, we can do something based on the hasFocus
boolean value.
You can combine multiple focusable widgets in a widget tree by wrapping each of them with FocusableActionDetector
. Here’s an example:
FocusableActionDetector(
onFocusChange: (hasFocus) {
if (hasFocus) {
// Do something when the widget gains focus
} else {
// Do something when the widget loses focus
}
},
actions: {
ActivateAction.key: CallbackAction(
onInvoke: () {
// Do something when the widget is activated
},
),
},
child: Container(
width: 100,
height: 100,
color: Colors.blue,
),
);
In this example, we have added two `FocusableActionDetector` widgets that wrap two `Container` widgets with different colors and texts. Each `FocusableActionDetector` widget has its own `actions` map that defines the “activate” action for each widget.
Here’s a complete code example that combines all the previous examples:
## Full code example
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'FocusableActionDetector Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('FocusableActionDetector Demo'),
),
body: Column(
children: [
FocusableActionDetector(
actions: {
ActivateAction.key: CallbackAction(
onInvoke: () {
print('Widget 1 activated');
},
),
},
onFocusChange: (hasFocus) {
if (hasFocus) {
print('Widget 1 has focus');
} else {
print('Widget 1 lost focus');
}
},
focusTraversalKeys: {
LogicalKeyboardKey.tab: TraversalDirectionalAction.increment,
LogicalKeyboardKey.shiftLeft: TraversalDirectionalAction.decrement,
},
child: Container(
width: 100,
height: 100,
color: Colors.blue,
child: Center(
child: Text('Widget 1'),
),
),
),
FocusableActionDetector(
actions: {
ActivateAction.key: CallbackAction(
onInvoke: () {
print('Widget 2 activated');
},
),
},
onFocusChange: (hasFocus) {
if (hasFocus) {
print('Widget 2 has focus');
} else {
print('Widget 2 lost focus');
}
},
focusTraversalKeys: {
LogicalKeyboardKey.tab: TraversalDirectionalAction.increment,
LogicalKeyboardKey.shiftLeft: TraversalDirectionalAction.decrement,
},
child: Container(
width: 100,
height: 100,
color: Colors.green,
child: Center(
child: Text('Widget 2'),
),
),
),
],
),
);
}
}
In this example, we have defined two FocusableActionDetector
widgets that wrap two Container
widgets with different colors and texts. We have also added onFocusChange
callbacks to handle focus events and focusTraversalKeys
maps to enable focus traversal using keyboard shortcuts. Finally, we have added ActivateAction
callbacks to handle widget activation events.