如何以编程方式设置DropdownButton的值?

时间:2019-10-16 20:16:24

标签: flutter

例如,为了在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时才能应用更改对象。

3 个答案:

答案 0 :(得分:0)

正如@CopsOnRoad所说,这里似乎没有快捷方式,必须调用setState才能反映DropdownButton所选值的变化。问题是,setStateprotected,所以我需要经过一些循环以确保在需要时调用它。最后,我实现了一个通告程序,该通告程序将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),
    ),
  );
}