在 Flutter 中强制重建打开的下拉列表

问题描述 投票:0回答:2

当执行“DropdownButtonFormField”小部件的构建方法时,如果用户已经单击它并且在 UI 中显示了选项,那么构建方法似乎不会更新这些选项。我尝试了各种方法并询问了 AI 无济于事; :-(

目前,小部件是

//...
import 'package:get_it/get_it.dart';
//...

class DynamicDropdown extends StatefulWidget {
  final void Function(ClientLItem) onSelected;

  const DynamicDropdown(
      {super.key,
      /*required Key key,*/ required this.onSelected}) /*: super(key: key)*/;

  @override
  _DynamicDropdownState createState() => _DynamicDropdownState();
}

class _DynamicDropdownState extends State<DynamicDropdown> {
  String name = '_DynamicDropdownState';
  ClientLItem? _selectedClient;
  String dropdownValue = "0";
  bool shouldUpdate = false;
  ClientListicle _clientListicle = GetIt.I.get<ClientListicle>();
  List<DropdownMenuItem<String>> menuItems = [
    const DropdownMenuItem(value: "0", child: Text('Select')),
  ];

  final _dropdownKey = GlobalKey<FormFieldState>();

  List<DropdownMenuItem<String>>? get dropdownItems {
    developer.log(
        'get dropdownItems() clients have  ${_clientListicle.items.length} clients',
        name: name);
    List<DropdownMenuItem<String>> newItems = [
      const DropdownMenuItem(value: "0", child: Text('Select')),
    ];

    for (var element in _clientListicle.items) {
      DropdownMenuItem<String> potentialItem = DropdownMenuItem(
          value: element.id.toString(), child: Text(element.title));
      newItems.add(potentialItem);
      developer.log('menu items count ${newItems.length}', name: name);
    }

    developer.log('new menu items count ${newItems.length}', name: name);
    if (newItems.length <= 1) {
      return null;
    } else {
      //if length of newitems differs from menu items schedule a forced rebuild
      if (newItems.length != menuItems.length) {
        menuItems = newItems;
        shouldUpdate = true;
        _onClientListicleUpdated();
        _dropdownKey.currentState!.build(context);
      }
      return menuItems;
    }
  }

  @override
  void initState() {
    developer.log('initState', name: name);
    super.initState();
    // Listen for changes in the ClientListicle instance
    _clientListicle.addListener(_onClientListicleUpdated);
    // if _clientListicle.items.isEmpty load some clients

    WidgetsBinding.instance.addPostFrameCallback((_) async {
      developer.log('addPostFrameCallback', name: name);
      if (_clientListicle.items.isEmpty) {
        fetchClients();
      }
      if (shouldUpdate) {
        shouldUpdate = false;
        setState(() {
          // update state here
        });
        _dropdownKey.currentState!.reset();
      }
    });
  }

  @override
  void dispose() {
    // Remove the listener when the widget is disposed
    _clientListicle.removeListener(_onClientListicleUpdated);
    super.dispose();
  }

  void _onClientListicleUpdated() {
    developer.log('_onClientListicleUpdated');
    // Call setState to rebuild the widget when the ClientListicle instance is updated
    setState(() {
      dropdownItems;
    });
    _dropdownKey.currentState!.reset();
  }

  @override
  State<StatefulWidget> createState() {
    developer.log('createState()');
    return _DynamicDropdownState();
  }

  @override
  Widget build(BuildContext context) {
    developer.log(
        'Build ClientListicle has ${_clientListicle.items.length} items',
        name: name);
    developer.log('dropdownItems has ${dropdownItems?.length} items',
        name: name);

    if (shouldUpdate) {
      developer.log('shouldUpdate is true', name: name);
      shouldUpdate = false;
      // Schedule a rebuild
      setState(() {});
    }

    return DropdownButtonFormField<String>(
      key: _dropdownKey,
      value: dropdownValue,
      icon: const Icon(Icons.keyboard_arrow_down),
      items: dropdownItems,
      validator: (value) => value == "0" ? 'Please select a client' : null,
      onChanged: (String? newValue) {
        if (newValue != null && newValue != "0") {
          developer.log('selected newValue $newValue', name: name);
          dropdownValue = newValue;
          _selectedClient =
              _clientListicle.getById(int.parse(newValue!)) as ClientLItem;
          setState(() {});
          widget.onSelected(_selectedClient!);
        } else {
          _selectedClient = null;
          dropdownValue = "0";
        }
        _dropdownKey.currentState!.reset();
      },
    );
  }

  Future<void> fetchClients() async {
    developer.log('fetchClients', name: name);
    await _clientListicle.fetch(numberToFetch: 5);
  }
}

根据日志输出,我可以看到例如

[_DynamicDropdownState] Build ClientListicle has 3 items
,但在数据到达之前单击下拉列表时,仍然只能看到可用的单个项目。

如果我在下拉菜单外单击并重新打开它,则会出现正确的项目,因此有状态小部件内的 setState 似乎不会强制重建 UI 中的选项列表。

flutter dart user-interface dropdown flutter-build
2个回答
0
投票

你没有明确说明你的问题。是否有两个下拉字段,一个用于客户,另一个用于客户特定选项?


0
投票

您可以尝试创建一个 StatefullWidget 并将下拉菜单放在那里,然后通过调用它的 setState 您应该能够更新下拉菜单项

© www.soinside.com 2019 - 2024. All rights reserved.