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

Flutter下拉菜单搜索栏弹窗偏移问题求助(dropdown_button2)

解决DropdownButton2搜索弹窗键盘弹出时的偏移问题

这个问题源于键盘弹出时页面整体上移,但DropdownButton2的下拉弹窗基于按钮初始位置渲染,未跟随按钮新位置更新。以下是两种可行的解决方式:


方式一:禁用页面自动上移(简单快捷)

在页面的Scaffold中添加resizeToAvoidBottomInset: false,阻止键盘弹出时页面上移,从根源避免弹窗错位:

return Scaffold(
  resizeToAvoidBottomInset: false, // 新增该行
  body: Center(
    // 原有页面代码
  ),
);

方式二:动态调整弹窗偏移(推荐,保留页面上移体验)

通过监听键盘高度变化,实时调整下拉弹窗的偏移量,确保弹窗始终对齐按钮:

1. 修改DropDownSearch组件代码

添加键盘监听逻辑,并动态设置弹窗偏移:

import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

  @override
  State<DropDownSearch> createState() => _DropDownSearchState();
}

class _DropDownSearchState extends State<DropDownSearch> with WidgetsBindingObserver {
  final List<String> items = [
    'A_Item1',
    'A_Item2',
    'A_Item3',
  ];

  String? selectedValue;
  final TextEditingController textEditingController = TextEditingController();
  double _keyboardHeight = 0; // 存储键盘高度

  @override
  void initState() {
    super.initState();
    WidgetsBinding.instance.addObserver(this); // 注册键盘监听
  }

  @override
  void dispose() {
    WidgetsBinding.instance.removeObserver(this); // 移除监听
    textEditingController.dispose();
    super.dispose();
  }

  @override
  void didChangeMetrics() {
    super.didChangeMetrics();
    // 获取键盘高度并更新状态
    final bottomInset = MediaQuery.of(context).viewInsets.bottom;
    setState(() {
      _keyboardHeight = bottomInset;
    });
  }

  @override
  Widget build(BuildContext context) {
    return DropdownButtonHideUnderline(
      child: DropdownButton2<String>(
        useRootNavigator: true, // 让弹窗在根导航层渲染,避免页面位移影响
        isExpanded: true,
        hint: Center(
          child: Text(
            '--Select EndUser--',
            style: TextStyle(
              fontSize: 14,
              color: Theme.of(context).hintColor,
            ),
          ),
        ),
        items: items
            .map((item) => DropdownMenuItem(
                  value: item,
                  child: Text(
                    item,
                    style: const TextStyle(
                      fontSize: 14,
                    ),
                  ),
                ))
            .toList(),
        value: selectedValue,
        onChanged: (value) {
          setState(() {
            selectedValue = value;
          });
        },
        buttonStyleData: ButtonStyleData(
            padding: EdgeInsets.symmetric(horizontal: 16),
            height: 50,
            width: 300,
            decoration: BoxDecoration(
              border: Border.all(color: Colors.black),
              borderRadius: BorderRadius.circular(10),
            )),
        dropdownStyleData: DropdownStyleData(
          maxHeight: 200,
          offset: Offset(0, -_keyboardHeight), // 根据键盘高度调整偏移
        ),
        menuItemStyleData: const MenuItemStyleData(
          height: 40,
        ),
        dropdownSearchData: DropdownSearchData(
          searchController: textEditingController,
          searchInnerWidgetHeight: 50,
          searchInnerWidget: Container(
            height: 50,
            padding: const EdgeInsets.only(
              top: 8,
              bottom: 4,
              right: 8,
              left: 8,
            ),
            child: TextFormField(
              expands: true,
              maxLines: null,
              controller: textEditingController,
              decoration: InputDecoration(
                isDense: true,
                contentPadding: const EdgeInsets.symmetric(
                  horizontal: 10,
                  vertical: 8,
                ),
                hintText: 'Search for an item...',
                hintStyle: const TextStyle(fontSize: 12),
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
              ),
            ),
          ),
          searchMatchFn: (item, searchValue) {
            return item.value.toString().contains(searchValue);
          },
        ),
        onMenuStateChange: (isOpen) {
          if (!isOpen) {
            textEditingController.clear();
          }
        },
      ),
    );
  }
}

原理说明

  • 键盘弹出时,MediaQuery.of(context).viewInsets.bottom会返回键盘的实际高度,页面上移的距离与该值一致。
  • 设置dropdownStyleData.offset为Offset(0, -_keyboardHeight),让弹窗向上移动对应距离,抵消页面上移带来的错位。
  • useRootNavigator: true确保弹窗在根导航器的Overlay层渲染,不受页面局部位移的影响。

内容的提问来源于stack exchange,提问作者Asim Reyazuddin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 13:04:49