You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter页面滚动时Dropdown选中值自动变更问题求助

解决Flutter Dropdown/Switch滚动时选中状态异常回退问题

问题根源

你的DropdownWidget把选中值存在了State的selectedValue变量里,但这个变量仅在State初始化时从widget.selectedValue赋值一次。当页面滚动触发ListView/GridView的item回收机制时,Widget会被销毁重建,State重新初始化后,selectedValue会被重置为父组件最初传入的旧值,而非更新后的新值,导致滚动后选中状态异常变更。Switch的问题逻辑完全一致——内部维护的选中状态在组件重建时被重置。


方案1:直接依赖父组件传入的状态(推荐)

去掉State里的缓存变量,让DropdownButton直接使用widget的属性,完全由父组件管理选中状态,组件重建时自动获取最新值:

class _DropdownWidgetState extends State<DropdownWidget> {
  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 50,
      width: double.infinity,
      child: ListTile(
        title: SizedBox(
          width: MediaQuery.of(context).size.width * 0.5,
          child: Row(
            children: [
              Text(
                widget.title,
                style: const TextStyle(
                    fontSize: 14,
                    fontFamily: 'Montserrat',
                    fontWeight: FontWeight.w600),
                overflow: TextOverflow.ellipsis,
              ),
              Visibility(
                  child: widget.title == 'Seating Capacity'
                      ? Text(
                          ' *',
                          style: TextStyle(color: AppColor.red),
                        )
                      : const Text(''))
            ],
          ),
        ),
        trailing: Container(
          width: MediaQuery.of(context).size.width * 0.40,
          height: 50,
          padding: const EdgeInsets.only(left: 10),
          decoration: BoxDecoration(
              color: AppColor.white,
              borderRadius: const BorderRadius.all(Radius.circular(7))),
          child: DropdownButtonHideUnderline(
            child: DropdownButton(
              value: widget.selectedValue,
              borderRadius: const BorderRadius.all(Radius.circular(7)),
              items: widget.dropdownList.map((String value) {
                return DropdownMenuItem(
                  value: value,
                  child: SizedBox(
                    width: MediaQuery.of(context).size.width * 0.30,
                    child: Text(
                      value.toString(),
                      style: TextStyle(
                          fontSize: 14,
                          fontFamily: 'Montserrat',
                          color: AppColor.black,
                          fontWeight: FontWeight.w500),
                      overflow: TextOverflow.clip,
                    ),
                  ),
                );
              }).toList(),
              onChanged: (String? selectedData) {
                widget.callback(selectedData);
              },
            ),
          ),
        ),
      ),
    );
  }
}

方案2:保留内部变量,同步父组件更新

如果需要在State内缓存变量,必须重写didUpdateWidget方法,当父组件传入的属性变化时同步更新内部状态:

class _DropdownWidgetState extends State<DropdownWidget> {
  late String selectedValue;
  late String title;
  late List<String> dropdownList;
  late Function callback;

  @override
  void initState() {
    super.initState();
    selectedValue = widget.selectedValue;
    title = widget.title;
    dropdownList = widget.dropdownList;
    callback = widget.callback;
  }

  @override
  void didUpdateWidget(covariant DropdownWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    // 同步父组件传入的选中值变化
    if (oldWidget.selectedValue != widget.selectedValue) {
      setState(() {
        selectedValue = widget.selectedValue;
      });
    }
    // 同步其他可能变化的属性
    if (oldWidget.title != widget.title) title = widget.title;
    if (oldWidget.dropdownList != widget.dropdownList) dropdownList = widget.dropdownList;
    if (oldWidget.callback != widget.callback) callback = widget.callback;
  }

  @override
  Widget build(BuildContext context) {
    // 原有build代码不变
  }
}

Switch组件修复方案

核心逻辑和Dropdown一致:将选中状态提到父组件管理,子组件仅作为展示层接收value和onChanged回调:

自定义Switch子组件

class CustomSwitch extends StatelessWidget {
  final bool value;
  final ValueChanged<bool> onChanged;
  final String title;

  const CustomSwitch({
    super.key,
    required this.value,
    required this.onChanged,
    required this.title,
  });

  @override
  Widget build(BuildContext context) {
    return ListTile(
      title: Text(title),
      trailing: Switch(
        value: value,
        onChanged: onChanged,
      ),
    );
  }
}

父组件中使用并管理状态

class ParentPage extends StatefulWidget {
  const ParentPage({super.key});

  @override
  State<ParentPage> createState() => _ParentPageState();
}

class _ParentPageState extends State<ParentPage> {
  bool _switchValue = false;

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: [
        CustomSwitch(
          value: _switchValue,
          onChanged: (newValue) {
            setState(() {
              _switchValue = newValue;
            });
          },
          title: "示例开关",
        ),
        // 其他列表项
      ],
    );
  }
}

核心原则

列表项的可变状态应该由父组件或全局状态管理工具(如Provider、Riverpod)持有,子组件仅作为无状态组件依赖外部传入的状态展示。这样无论列表如何滚动重建,子组件都会获取到最新的状态值,避免异常回退。

内容的提问来源于stack exchange,提问作者Mari Selvan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.14 15:01:01