例如,为了在TextFormField
中设置文本,我可以使用TextEditingController
:
textEditingController = TextEditingController()
...
TextFormField(
controller: textEditingController
);
...
textEditingController.text = 'my text'; // This is where I can set the text in the TextFormField
是否有类似的方法以编程方式在DropdownButton
中设置选择?据我所知,仅在value
中设置DropdownButton
字段是不够的,因为只有在包装setState
中调用state
时才能应用更改对象。
答案 0 :(得分:0)
正如@CopsOnRoad所说,这里似乎没有快捷方式,必须调用setState
才能反映DropdownButton
所选值的变化。问题是,setState
是protected
,所以我需要经过一些循环以确保在需要时调用它。最后,我实现了一个通告程序,该通告程序将DropdownButton
的状态作为侦听器。遵循以下原则:
class MyStatefulWidget extends StatefulWidget {
final _valueNotifier = ValueNotifier<String>(null);
@override
State<StatefulWidget> createState() => MyState(_valueNotifier);
// This exposes the ability to change the DropdownButtons's value
void setDropdownValue(String value) {
// This will notify the state and will eventually call setState
_valueNotifier.value = value;
}
}
class MyState extends State<MyStatefulWidget> {
String _selection;
MyState(ValueNotifier<String> valueNotifier) {
valueNotifier.addListener(() {
setState(() {
_selection = valueNotifier.value;
});
});
}
@override
Widget build(BuildContext context) {
return DropdownButton<String>(
items: [
DropdownMenuItem<String>(
value: "1",
child: Text(
"1",
),
),
DropdownMenuItem<String>(
value: "2",
child: Text(
"2",
),
)
],
onChanged: (value) {
setState(() {
_selection = value;
});
},
value: _selection,
);
}
}
答案 1 :(得分:0)
如果您将逻辑与ui分开,并通过ui侦听的流传递事件,则可以使用setState解决问题,并且逻辑更易于使用。
StreamBuilder是一个很棒的小部件,如果您习惯使用它,可以大大简化ui代码。本质上,每次有新值通过流时,都会重新运行builder函数,并且可以在snapshot.data中找到放入流中的任何内容(如新的下拉按钮值)。
这是一个例子:
在用户界面中,您可以像这样构建下拉按钮:
StreamBuilder<String>(
stream: logicClass.dropdownValueStream,
builder: (context, snapshot) {
return DropdownButton(
items: logicClass.menuItems,
onChanged: logicClass.selectItem,
value: snapshot.data,
);
})
,在逻辑类中,您将像这样构建流:
StreamController<String> _dropDownController = StreamController<String>();
Stream<String> get dropdownValueStream => _dropDownController.stream;
Function get selectItem => _dropDownController.sink.add;
最后,如果您想对这些数据做任何事情,您可以将其存储在逻辑流中,并将其存储在逻辑类中。这实际上是UI逻辑与业务逻辑或Bloc模式的分离。
答案 2 :(得分:0)
我创建了一个简化的DropdownButton
以能够使用控制器,它可以像这样使用:
SimpleDropdownButton(
values: _values,
itemBuilder: (value) => Text(value),
controller: _controller,
onChanged: (value) => print(_controller.value),
)
基本上,SimpleDropdownButton
会包装DropdownButton
并根据收到的值列表和您要显示这些值的方式来处理DropdownItems
的创建。
如果您未设置控制器,则SimpleDropdownButton
会像处理DropdownButton
一样使用setState()
处理选定的值。
如果确实设置了控制器,则SimpleDropdownButton
将开始侦听控制器,以了解何时调用setState()
以更新所选值。因此,如果有人选择了一个项目(onChanged
,则SimpleDropdownButton
不会调用setState()
,而是会为控制器设置新值,并且控制器将通知侦听器,其中之一监听者是SimpleDropdownButton
,他们将调用setState()
更新所选值。这样,如果您为控制器设置新值,则将通知SimpleDropdownButton
。另外,由于该值始终存储在控制器上,因此可以随时对其进行访问。
这是实现,您可能需要将更多参数传递给DropdownButton
:
class SimpleDropdownButton<T> extends StatefulWidget {
final List<T> values;
final Widget Function(T value) itemBuilder;
final SimpleDropdownButtonController<T> controller;
final ValueChanged onChanged;
SimpleDropdownButton(
{this.controller,
@required this.values,
@required this.itemBuilder,
this.onChanged});
@override
_SimpleDropdownButtonState<T> createState() =>
_SimpleDropdownButtonState<T>();
}
class _SimpleDropdownButtonState<T> extends State<SimpleDropdownButton<T>> {
T _value;
@override
void initState() {
super.initState();
if (widget.controller != null) {
_value = widget.controller.value;
widget.controller.addListener(() => setState(() {
_value = widget.controller.value;
}));
}
}
@override
void dispose() {
widget.controller?.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return DropdownButton(
value: _value,
items: widget.values
.map((value) => DropdownMenuItem(
value: value,
child: widget.itemBuilder(value),
))
.toList(),
onChanged: (value) {
if (widget.controller != null) {
widget.controller.value = value;
} else {
setState(() {
_value = value;
});
}
widget.onChanged?.call(value);
},
);
}
}
class SimpleDropdownButtonController<T> {
List<VoidCallback> _listeners = [];
T _value;
SimpleDropdownButtonController([this._value]);
get value => _value;
set value(T value) {
_value = value;
_listeners?.forEach((listener) => listener());
}
void addListener(VoidCallback listener) => _listeners.add(listener);
void close() => _listeners?.clear();
}
以及使用它的示例:
final _values = ["Value 1", "Value 2", "Value 3", "Value 4"];
final _controller = SimpleDropdownButtonController("Value 1");
@override
Widget build(BuildContext context) {
print('build()');
return Scaffold(
appBar: AppBar(title: Text("SimpleDropdownButton")),
floatingActionButton: FloatingActionButton(
onPressed: () => _controller.value = "Value 3",
),
body: SimpleDropdownButton(
values: _values,
itemBuilder: (value) => Text(value),
controller: _controller,
onChanged: (value) => print(_controller.value),
),
);
}