无法更改下拉按钮值颤振

Posted

技术标签:

【中文标题】无法更改下拉按钮值颤振【英文标题】:Cannot change Dropdown button value Flutter 【发布时间】:2021-10-24 06:30:39 【问题描述】:

我想从 firebase 获取 Dropdown 按钮的初始值; 但是当我尝试在构建方法中设置governorateDDValue = selectedUser.governorate; Dropdown 的值从 firebase 获取值,但我无法更改它

DropdownButton.gif

这是我的代码

class UserInfo extends StatefulWidget 
  static const routeName = '/UserInfoScreen';

  const UserInfo(Key? key) : super(key: key);

  @override
  _UserInfoState createState() => _UserInfoState();


class _UserInfoState extends State<UserInfo> 

  late User selectedUser;
  final date = DateFormat('yyyy-MM-dd').format(DateTime.now()).toString();

  var governorateDDValue;

  @override
  Widget build(BuildContext context) 

    final userList= Provider.of<List<User>>(context);
    final userID = ModalRoute.of(context)!.settings.arguments as String;
    selectedUser =
        userList.firstWhere((user) => user.id == userID);

    // this line makes dropdown value always equal to value from firestore
    governorateDDValue = selectedUser.governorate;

    return Scaffold(
      appBar: AppBar(
        title: Text('report'),
      ),
      body: SingleChildScrollView(
        child: Container(
          child: Row(
            children: <Widget>[
                    Text('Governorate',),
                    Container(height: 5),
                    DropdownButton<String>(
                      value: governorateDDValue,
                      icon: const Icon(Icons.arrow_downward),
                      iconSize: 24,
                      elevation: 16,
                      style: const TextStyle(color: Colors.deepPurple),
                      onChanged: (String? newValue) 
                        setState(() 
                          governorateDDValue = newValue!;
                        );
                      ,
                      items: Constants.governoratesOfEgypt
                          .map<DropdownMenuItem<String>>((String value) 
                        return DropdownMenuItem<String>(
                          value: value,
                          child: Text(value),
                        );
                      ).toList(),
          ),
        ],
      ),
    ),
  ),
);

提前致谢

【问题讨论】:

【参考方案1】:

因为您在构建小部件中使用governorateDDValue = selectedUser.governorate;,所以每次更改下拉菜单时都会重置其值 构建小部件将重建,下拉列表的值将保持等于 firebase 的值 您应该在构建小部件之外使用governorateDDValue = selectedUser.governorate; 这段代码应该可以工作

class UserInfo extends StatefulWidget 
  static const routeName = '/UserInfoScreen';

  const UserInfo(Key? key) : super(key: key);

  @override
  _UserInfoState createState() => _UserInfoState();


class _UserInfoState extends State<UserInfo> 

  var loading = false;

  late User selectedUser;
  final date = DateFormat('yyyy-MM-dd').format(DateTime.now()).toString();

  var governorateDDValue;


  @override
  void initState() 
    super.initState();
    loading = true;
    print('Future.delayed outside');
    print(loading);
    Future.delayed(Duration.zero, () 
      governorateDDValue = selectedUser.governorate;
      setState(() 
        loading = false;
      );
    );
  


  @override
  Widget build(BuildContext context) 
    final userList = Provider.of<List<User>>(context);
    final userID = ModalRoute
        .of(context)!
        .settings
        .arguments as String;
    selectedUser =
        userList.firstWhere((user) => user.id == userID);

    // this line makes dropdown value always equal to value from firestore
    governorateDDValue = selectedUser.governorate;

    return Scaffold(
      appBar: AppBar(
        title: Text('report'),
      ),
      body: SingleChildScrollView(
        child: Container(
          child: Row(
            children: <Widget>[
              Text('Governorate',),
              Container(height: 5),
              DropdownButton<String>(
                value: governorateDDValue,
                icon: const Icon(Icons.arrow_downward),
                iconSize: 24,
                elevation: 16,
                style: const TextStyle(color: Colors.deepPurple),
                onChanged: (String? newValue) 
                  setState(() 
                    governorateDDValue = newValue!;
                  );
                ,
                items: Constants.governoratesOfEgypt
                    .map<DropdownMenuItem<String>>((String value) 
                  return DropdownMenuItem<String>(
                    value: value,
                    child: Text(value),
                  );
                ).toList(),
              ),
            ],
          ),
        ),
      ),
    );

【讨论】:

【参考方案2】:

之所以不能更改它是因为每次调用setState 时都会调用您的build 方法。因此,您的值将始终设置为 governorateDDValue = selectedUser.governorate; 所以要允许更改,您应该将此 governorateDDValue = selectedUser.governorate; 放在 iniState

或者你可以这样做,这样它只会设置一次

class UserInfo extends StatefulWidget 
  static const routeName = '/UserInfoScreen';

  const UserInfo(Key? key) : super(key: key);

  @override
  _UserInfoState createState() => _UserInfoState();


class _UserInfoState extends State<UserInfo> 

  late User selectedUser;
  final date = DateFormat('yyyy-MM-dd').format(DateTime.now()).toString();
  bool initState = true; // ADD HERE
  var governorateDDValue;

  @override
  Widget build(BuildContext context) 

    final userList= Provider.of<List<User>>(context);
    final userID = ModalRoute.of(context)!.settings.arguments as String;
    selectedUser =
        userList.firstWhere((user) => user.id == userID);

    // this line makes dropdown value always equal to value from firestore
    if(initState)  // ADD HERE
    governorateDDValue = selectedUser.governorate; 
    initState = false;  // ADD HERE
    

    return Scaffold(
      appBar: AppBar(
        title: Text('report'),
      ),
      body: SingleChildScrollView(
        child: Container(
          child: Row(
            children: <Widget>[
                    Text('Governorate',),
                    Container(height: 5),
                    DropdownButton<String>(
                      value: governorateDDValue,
                      icon: const Icon(Icons.arrow_downward),
                      iconSize: 24,
                      elevation: 16,
                      style: const TextStyle(color: Colors.deepPurple),
                      onChanged: (String? newValue) 
                        setState(() 
                          governorateDDValue = newValue!;
                        );
                      ,
                      items: Constants.governoratesOfEgypt
                          .map<DropdownMenuItem<String>>((String value) 
                        return DropdownMenuItem<String>(
                          value: value,
                          child: Text(value),
                        );
                      ).toList(),
          ),
        ],
      ),
    ),
  ),
);


【讨论】:

我把这个governanceDDValue = selectedUser.governorate;在 iniState 它给了我这个错误 LateInitializationError: Field 'selectedUser' has not been initialized。当我删除“selectedUser = userList.firstWhere((user) => user.id == userID);”到 initState 它给了我另一个错误在构建 Builder 时抛出了以下断言:在 _UserInfoState.initState() 完成之前调用了dependOnInheritedWidgetOfExactType<_modalscopestatus>() 或dependOnInheritedElement()。 您必须将所有逻辑移动到 iniState 中,如果您需要提供者的上下文,您可以使用 didChangeDependencies 我已经编辑了答案以获得更简单的解决方案

以上是关于无法更改下拉按钮值颤振的主要内容,如果未能解决你的问题,请参考以下文章

选择值时出现颤振下拉按钮错误[使用来自 api 和 FutureBuilder 的数据]

无法在 Flutter 中创建下拉按钮

Flutter:如何根据选择的第一个下拉按钮值设置第二个下拉按钮(多级相关按钮)的值?

无法更改标题中的按钮标题

无法在颤动中更改按钮的高度

选择值后如何将下拉菜单转换为标签。单击按钮后再次转换为下拉菜单