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

Flutter BottomSheet弹窗问题求助:键盘动效异常与PopMenu位置偏移

Flutter BottomSheet 问题修复方案

一、修复键盘适配动效生硬的问题

  1. 删掉Duration.zero,给AnimatedPadding设置符合系统节奏的动画时长和曲线,比如Duration(milliseconds: 200)搭配Curves.easeInOut,和键盘弹出的动画节奏对齐
  2. 不要给Container固定height:160,让它由子内容撑开(用Column+MainAxisSize.min包裹输入框和按钮即可),固定高度会导致padding变化时布局跳动突兀
  3. 也可以尝试用DraggableScrollableSheet替代普通BottomSheet,它对键盘的适配逻辑更自然

修改后的AnimatedPadding代码示例:

AnimatedPadding(
  padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom),
  duration: const Duration(milliseconds: 200),
  curve: Curves.easeInOut,
  child: Container(
    padding: widget.padding,
    decoration: const BoxDecoration(
      borderRadius: BorderRadius.only(
        topLeft: Radius.circular(16),
        topRight: Radius.circular(16),
      ),
      color: Colors.white,
    ),
    child: Column(
      mainAxisSize: MainAxisSize.min,
      children: [
        // 你的输入框
        TextField(
          autofocus: true,
          decoration: const InputDecoration(hintText: "输入内容"),
        ),
        ElevatedButton(
          onPressed: () {
            // 后续逻辑
          },
          child: const Text("选择优先级"),
        ),
      ],
    ),
  ),
)

二、修复PopMenu弹出时BottomSheet回缩的问题

问题出在你调用了currentFocus.unfocus(),这会触发键盘收起,导致MediaQuery.viewInsets.bottom变为0,BottomSheet跟着回缩,进而让PopMenu位置错误。解决办法:

  1. 不要主动取消焦点:保留输入框的焦点,避免键盘收起
  2. 基于按钮的上下文计算位置:弹出PopMenu时,通过按钮的RenderBox获取准确坐标,不要依赖BottomSheet的上下文
  3. 手动指定PopMenu的位置:用RelativeRect.fromRect把PopMenu固定在按钮下方

修改后的按钮点击逻辑:

ElevatedButton(
  onPressed: () {
    // 移除unfocus代码,保留输入框焦点
    final buttonRenderBox = context.findRenderObject() as RenderBox;
    final overlayRenderBox = Overlay.of(context).context.findRenderObject() as RenderBox;
    final menuPosition = RelativeRect.fromRect(
      Rect.fromPoints(
        buttonRenderBox.localToGlobal(Offset.zero, ancestor: overlayRenderBox),
        buttonRenderBox.localToGlobal(buttonRenderBox.size.bottomRight(Offset.zero), ancestor: overlayRenderBox),
      ),
      Offset.zero & overlayRenderBox.size,
    );

    showMenu(
      context: context,
      position: menuPosition,
      items: const [
        PopupMenuItem(value: 1, child: Text("优先级1")),
        PopupMenuItem(value: 2, child: Text("优先级2")),
        PopupMenuItem(value: 3, child: Text("优先级3")),
      ],
    ).then((selectedPriority) {
      if (selectedPriority != null) {
        setState(() {
          priority = selectedPriority;
        });
      }
    });
  },
  child: const Text("选择优先级"),
)

额外优化建议

  • 如果必须在PopMenu弹出时隐藏键盘,可以先计算好按钮位置再取消焦点,选完后重新获取焦点:
onPressed: () {
  // 先计算位置
  final buttonRenderBox = context.findRenderObject() as RenderBox;
  final overlayRenderBox = Overlay.of(context).context.findRenderObject() as RenderBox;
  final menuPosition = RelativeRect.fromRect(...);
  
  // 再取消焦点
  FocusScope.of(context).unfocus();
  
  showMenu(...).then((selectedPriority) {
    if (selectedPriority != null) {
      setState(() {
        priority = selectedPriority;
      });
      // 重新获取焦点
      _inputFocusNode.requestFocus();
    }
  });
}
  • 把输入框的FocusNode抽成组件的成员变量,控制焦点更灵活:
final FocusNode _inputFocusNode = FocusNode();

@override
void initState() {
  super.initState();
  // 初始化后自动获焦
  WidgetsBinding.instance.addPostFrameCallback((_) {
    _inputFocusNode.requestFocus();
  });
}

// 输入框使用这个FocusNode
TextField(focusNode: _inputFocusNode, ...)

@override
void dispose() {
  _inputFocusNode.dispose();
  super.dispose();
}

内容的提问来源于stack exchange,提问作者reus wang

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 11:37:47