如何立即切换焦点小组(通过键盘)?
How do I switch focus groups (via keyboard) immediately?
当从一个焦点组移动到下一个焦点组(使用键盘上的选项卡)时,我希望焦点移动到下一组中的第一个字段,但它似乎没有关注任何内容 - 然后另一个选项卡移动到那个组.
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < 2; i++)
FocusableActionDetector(
onFocusChange: (focused) {
if (!focused) {
print('Have left focus group');
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var i = 0; i < 3; i++)
SizedBox(
width: 150,
child: TextField(),
),
],
),
),
],
),
),
),
);
}
}
我希望在第一组中的最后一个字段之后按 Tab
应该立即将焦点移动到下一个焦点组中的第一个项目。
我尝试了各种FocusNode
、FocusScope
、FocusScope.of(context).___
,但是我发现 Flutter 中的焦点管理有点混乱。
前言:我不是 Focus
方面的专家,我自己也觉得它很复杂。
但我相信下面的内容可以满足您的需求,从一列遍历到另一列,而不关注“不可见”项目。至少在移动设备上。网络平台...这是一个完全不同的球赛(我怀疑它的工作原理是否相同)。
我摆脱了 FocusableActionDetector
(它本身充当 FocusNode
)并将每个列包装在 FocusTraversalGroup
中。我相信 Flutter 会尽可能地尝试从 TraversalGroup 转到 TraversalGroup。
包裹 TraversalGroups 的 FocusScope
可防止后退按钮和任何其他可点击项目获得焦点(一旦 FocusScope
获得焦点)。
class MyHomePage2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: double.infinity,
child: FocusScope( // LIMIT FOCUS TO DESCENDANTS
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < 2; i++)
FocusTraversalGroup( // CREATE GROUPS HERE
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var i = 0; i < 3; i++)
SizedBox(
width: 150,
child: TextField(),
),
],
),
),
],
),
),
),
),
);
}
}
总结
(据我所知,这是模糊的)
FocusScope
限制节点遍历到它的直系后代节点
- 焦点不会从一个
FocusScope
遍历到另一个FocusScope
- 要转到另一个
FocusScope
,用户需要 manually/click-focus 另一个 FocusScope
,或其后代节点必须 requestFocus
- 使用
FocusScope
而不是上面的 FocusTraversalGroup
,会将节点遍历限制为一个 Column
,以获得焦点为准。当到达最后一个 TextField
时,它不会从一个 Column
跳到下一个
FocusTraversalGroup
将后代收集到一个组中进行遍历
- 但是 focus 会离开这个组去另一个组
- 在最后一个子节点和另一个
FocusTraversalGroup
可用时跳转
- 可以调整其组内的遍历顺序
调试
使用 debugDumpFocusTree
(随处可用的静态函数)转储国策树有助于调试。
我有时将它添加到 AppBar 以便于按需访问:
return Scaffold(
appBar: AppBar(
title: Text('Focus Tab Page'),
actions: [
IconButton(icon: Icon(Icons.info_outline), onPressed: debugDumpFocusTree)
],
),
我已经复制到下面的相关部分。
└─Child 2: FocusScopeNode#c83f0(_ModalScopeState<dynamic> Focus Scope [IN FOCUS PATH])
│ context: FocusScope
│ IN FOCUS PATH
│ focusedChildren: FocusScopeNode#7d536([IN FOCUS PATH])
│
├─Child 1: FocusScopeNode#7d536([IN FOCUS PATH])
│ │ context: FocusScope
│ │ IN FOCUS PATH
│ │ focusedChildren: FocusNode#1b886([PRIMARY FOCUS]),
│ │ FocusNode#72c3b, FocusNode#34b25, FocusNode#3b410,
│ │ FocusNode#02fac, FocusNode#61cd5
│ │
│ ├─Child 1: FocusNode#1d51e(FocusTraversalGroup)
│ │ │ context: Focus
│ │ │ NOT FOCUSABLE
│ │ │
│ │ ├─Child 1: FocusNode#3b410
│ │ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#70699]
│ │ │
│ │ ├─Child 2: FocusNode#34b25
│ │ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#7c822]
│ │ │
│ │ └─Child 3: FocusNode#72c3b
│ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#f00bc]
│ │
│ └─Child 2: FocusNode#bdb17(FocusTraversalGroup [IN FOCUS PATH])
│ │ context: Focus
│ │ NOT FOCUSABLE
│ │ IN FOCUS PATH
│ │
│ ├─Child 1: FocusNode#1b886([PRIMARY FOCUS])
│ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#c5a56]
│ │ PRIMARY FOCUS
│ │
│ ├─Child 2: FocusNode#61cd5
│ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#a4dd8]
│ │
│ └─Child 3: FocusNode#02fac
│ context: EditableText-[LabeledGlobalKey<EditableTextState>#fe12d]
│
├─Child 2: FocusNode#4886e
│ context: Focus
│
└─Child 3: FocusNode#9241b
context: Focus
当从一个焦点组移动到下一个焦点组(使用键盘上的选项卡)时,我希望焦点移动到下一组中的第一个字段,但它似乎没有关注任何内容 - 然后另一个选项卡移动到那个组.
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: double.infinity,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < 2; i++)
FocusableActionDetector(
onFocusChange: (focused) {
if (!focused) {
print('Have left focus group');
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var i = 0; i < 3; i++)
SizedBox(
width: 150,
child: TextField(),
),
],
),
),
],
),
),
),
);
}
}
我希望在第一组中的最后一个字段之后按 Tab
应该立即将焦点移动到下一个焦点组中的第一个项目。
我尝试了各种FocusNode
、FocusScope
、FocusScope.of(context).___
,但是我发现 Flutter 中的焦点管理有点混乱。
前言:我不是 Focus
方面的专家,我自己也觉得它很复杂。
但我相信下面的内容可以满足您的需求,从一列遍历到另一列,而不关注“不可见”项目。至少在移动设备上。网络平台...这是一个完全不同的球赛(我怀疑它的工作原理是否相同)。
我摆脱了 FocusableActionDetector
(它本身充当 FocusNode
)并将每个列包装在 FocusTraversalGroup
中。我相信 Flutter 会尽可能地尝试从 TraversalGroup 转到 TraversalGroup。
包裹 TraversalGroups 的 FocusScope
可防止后退按钮和任何其他可点击项目获得焦点(一旦 FocusScope
获得焦点)。
class MyHomePage2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: double.infinity,
child: FocusScope( // LIMIT FOCUS TO DESCENDANTS
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
for (var i = 0; i < 2; i++)
FocusTraversalGroup( // CREATE GROUPS HERE
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (var i = 0; i < 3; i++)
SizedBox(
width: 150,
child: TextField(),
),
],
),
),
],
),
),
),
),
);
}
}
总结
(据我所知,这是模糊的)
FocusScope
限制节点遍历到它的直系后代节点- 焦点不会从一个
FocusScope
遍历到另一个FocusScope
- 要转到另一个
FocusScope
,用户需要 manually/click-focus 另一个FocusScope
,或其后代节点必须requestFocus
- 使用
FocusScope
而不是上面的FocusTraversalGroup
,会将节点遍历限制为一个Column
,以获得焦点为准。当到达最后一个TextField
时,它不会从一个
Column
跳到下一个- 焦点不会从一个
FocusTraversalGroup
将后代收集到一个组中进行遍历- 但是 focus 会离开这个组去另一个组
- 在最后一个子节点和另一个
FocusTraversalGroup
可用时跳转 - 可以调整其组内的遍历顺序
调试
使用 debugDumpFocusTree
(随处可用的静态函数)转储国策树有助于调试。
我有时将它添加到 AppBar 以便于按需访问:
return Scaffold(
appBar: AppBar(
title: Text('Focus Tab Page'),
actions: [
IconButton(icon: Icon(Icons.info_outline), onPressed: debugDumpFocusTree)
],
),
我已经复制到下面的相关部分。
└─Child 2: FocusScopeNode#c83f0(_ModalScopeState<dynamic> Focus Scope [IN FOCUS PATH])
│ context: FocusScope
│ IN FOCUS PATH
│ focusedChildren: FocusScopeNode#7d536([IN FOCUS PATH])
│
├─Child 1: FocusScopeNode#7d536([IN FOCUS PATH])
│ │ context: FocusScope
│ │ IN FOCUS PATH
│ │ focusedChildren: FocusNode#1b886([PRIMARY FOCUS]),
│ │ FocusNode#72c3b, FocusNode#34b25, FocusNode#3b410,
│ │ FocusNode#02fac, FocusNode#61cd5
│ │
│ ├─Child 1: FocusNode#1d51e(FocusTraversalGroup)
│ │ │ context: Focus
│ │ │ NOT FOCUSABLE
│ │ │
│ │ ├─Child 1: FocusNode#3b410
│ │ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#70699]
│ │ │
│ │ ├─Child 2: FocusNode#34b25
│ │ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#7c822]
│ │ │
│ │ └─Child 3: FocusNode#72c3b
│ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#f00bc]
│ │
│ └─Child 2: FocusNode#bdb17(FocusTraversalGroup [IN FOCUS PATH])
│ │ context: Focus
│ │ NOT FOCUSABLE
│ │ IN FOCUS PATH
│ │
│ ├─Child 1: FocusNode#1b886([PRIMARY FOCUS])
│ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#c5a56]
│ │ PRIMARY FOCUS
│ │
│ ├─Child 2: FocusNode#61cd5
│ │ context: EditableText-[LabeledGlobalKey<EditableTextState>#a4dd8]
│ │
│ └─Child 3: FocusNode#02fac
│ context: EditableText-[LabeledGlobalKey<EditableTextState>#fe12d]
│
├─Child 2: FocusNode#4886e
│ context: Focus
│
└─Child 3: FocusNode#9241b
context: Focus