DropdownButton当点击其他小部件时,我需要以编程方式打开/显示a的选项列表。我知道这可能不是 UI 最佳实践,但我需要这种行为:
例如,在如下所示的结构中,我可能需要通过录音Text("every")来打开相邻DropdownButton的下拉列表,其行为类似于<select>在 HTML 中单击 a的标签。
Row(children: [
Padding(
padding: const EdgeInsets.only(right: 16),
child: Text('every'),
),
Expanded(
child: DropdownButton<String>(
value: _data['every'],
onChanged: (String val) => setState(() => _data['every'] = val),
items: _every_options.map<DropdownMenuItem<String>>(
(String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
},
).toList(),
isExpanded: true,
),
),
]);
Run Code Online (Sandbox Code Playgroud)
注意:我需要这个问题的一般解决方案,而不仅仅是如何Text在下面的树中使其表现得有点“像 HTML 标签”。它可能需要通过更远的按钮等触发才能打开。
这是(许多)设计的 API 限制之一......
完成您想要的最简单方法,无需修改 SDK,复制dropdown.dart,并创建您自己的版本,比如说custom_dropdown.dart,然后将代码粘贴到那里...
在第 546 行,将类重命名为CustomDropdownButton,在第 660 和 663 行将_DropdownButtonState重命名为CustomDropdownButtonState,(我们需要将状态类暴露在文件外)。
现在你可以用它做任何你想做的事情,虽然你对 _handleTap() 感兴趣,但可以打开覆盖菜单选项。
与其将 _handleTap() 设为公开并重构代码,不如添加另一个方法,例如:
(line 726)
void callTap() => _handleTap();
Run Code Online (Sandbox Code Playgroud)
现在,更改您的代码以使用 DropdownButton 而不是 Flutter 的 DropdownButton,关键是“设置键”(全局一个):P
// 一些有状态的小部件实现。
Map<String, String> _data;
List<String> _every_options;
// we need the globalKey to access the State.
final GlobalKey dropdownKey = GlobalKey();
@override
void initState() {
_every_options = List.generate(10, (i) => "item $i");
_data = {'every': _every_options.first};
simulateClick();
super.initState();
}
@override
Widget build(BuildContext context) {
return SafeArea(
child: Row(children: [
Padding(
padding: const EdgeInsets.only(right: 16),
child: Text('every'),
),
Expanded(
child: CustomDropdownButton<String>(
key: dropdownKey,
value: _data['every'],
onChanged: (String val) => setState(() => _data['every'] = val),
items: _every_options
.map((str) => DropdownMenuItem(
value: str,
child: Text(str),
))
.toList(),
isExpanded: true,
),
),
]),
);
}
void simulateClick() {
Timer(Duration(seconds: 2), () {
// here's the "magic" to retrieve the state... not very elegant, but works.
CustomDropdownButtonState state = dropdownKey.currentState;
state.callTap();
});
}
Run Code Online (Sandbox Code Playgroud)
另一个答案是最好的方法,但正如 OP 在评论中所要求的那样,这里有两种非常“hacky”的方法来实现这一点,但没有实现自定义小部件。
1.DropdownButton直接使用访问小部件树GlobalKey
如果我们查看 的源代码DropdownButton,我们可以注意到它用于GestureDetector处理点击。然而,它不是 的直接后代DropdownButton,我们不能依赖于其他小部件的树结构,所以找到检测器的唯一合理稳定的方法是递归搜索。
一个例子胜过一千个解释:
class DemoDropdown extends StatefulWidget {
@override
InputDropdownState createState() => DemoDropdownState();
}
class DemoDropdownState<T> extends State<DemoDropdown> {
/// This is the global key, which will be used to traverse [DropdownButton]s widget tree
GlobalKey _dropdownButtonKey;
void openDropdown() {
GestureDetector detector;
void searchForGestureDetector(BuildContext element) {
element.visitChildElements((element) {
if (element.widget != null && element.widget is GestureDetector) {
detector = element.widget;
return false;
} else {
searchForGestureDetector(element);
}
return true;
});
}
searchForGestureDetector(_dropdownButtonKey.currentContext);
assert(detector != null);
detector.onTap();
}
@override
Widget build(BuildContext context) {
final dropdown = DropdownButton<int>(
key: _dropdownButtonKey,
items: [
DropdownMenuItem(value: 1, child: Text('1')),
DropdownMenuItem(value: 2, child: Text('2')),
DropdownMenuItem(value: 3, child: Text('3')),
],
onChanged: (int value) {},
);
return Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Offstage(child: dropdown),
FlatButton(onPressed: openDropdown, child: Text('CLICK ME')),
],
);
}
}
Run Code Online (Sandbox Code Playgroud)
2. 使用 Actions.invoke
Flutter 的最新功能之一是Actions(我不确定它的含义,我今天才注意到它flutter upgrade),并DropdownButton使用它来对不同的......好吧,动作做出反应。
因此,触发按钮的稍微不那么笨拙的方法是找到Actions小部件的上下文并调用必要的操作。
这种方法有两个优点:首先,Actions小部件在树中的位置稍高,因此遍历该树不会像 with 一样长GestureDetector,其次,Actions似乎是一种比手势检测更通用的机制,因此不太可能从DropdownButton未来消失。
// The rest of the code is the same
void openDropdown() {
_dropdownButtonKey.currentContext.visitChildElements((element) {
if (element.widget != null && element.widget is Semantics) {
element.visitChildElements((element) {
if (element.widget != null && element.widget is Actions) {
element.visitChildElements((element) {
Actions.invoke(element, Intent(ActivateAction.key));
return false;
});
}
});
}
});
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8170 次 |
| 最近记录: |