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

Flutter中Stack嵌套GoogleMap、DSS及Align组件的键盘适配问题

Flutter Stack布局下键盘弹出导致地图与DraggableScrollableSheet异常的解决办法

问题场景与症状

  • 布局结构:Stack嵌套GoogleMap、带自定义按钮的Align、DraggableScrollableSheet(简称DSS),顶部通过Positioned放置PlacesSearchField搜索栏
  • 预期行为:点击搜索栏时,DSS展开至屏幕高度的0.75,键盘弹出,GoogleMap保持原尺寸不缩放
  • 实际异常:键盘弹出时Stack被缩放,引发以下问题:
    1. GoogleMap和Align组件随Stack收缩上移
    2. DSS与搜索栏交互异常,常出现短暂展开后立即收起的情况
    3. 偶尔达到预期效果时,GoogleMap会抖动,DSS基于新屏幕尺寸的0.75高度会遮挡小屏设备的搜索栏,顶部出现短暂灰色背景,体验卡顿

已尝试无效的方案

  • 设置Scaffold的resizeToAvoidBottomInset: false,键盘仍会导致Stack缩放
  • 用MediaQuery包裹GoogleMap并设置viewInsets: EdgeInsets.zero,问题未解决

有效解决方案

核心思路是将Stack固定为屏幕原始尺寸,避免键盘触发布局缩放,同时手动控制DSS与键盘的交互逻辑。

步骤1:固定Stack的屏幕尺寸

通过MediaQuery获取屏幕原始宽高,给Stack设置固定尺寸,确保键盘弹出时Stack不会被压缩:

final screenSize = MediaQuery.of(context).size;

return Scaffold(
  resizeToAvoidBottomInset: false, // 保留该设置
  body: SizedBox(
    width: screenSize.width,
    height: screenSize.height,
    child: Stack(
      children: [
        // 固定尺寸的GoogleMap容器
        SizedBox(
          width: screenSize.width,
          height: screenSize.height,
          child: GoogleMap(
            // 你的地图配置参数
          ),
        ),
        // 带自定义按钮的Align组件
        Align(
          alignment: Alignment.bottomRight,
          child: YourCustomButton(),
        ),
        // 顶部搜索栏
        Positioned(
          top: 16,
          left: 16,
          right: 16,
          child: PlacesSearchField(
            focusNode: _searchFocusNode,
            onTap: () => _searchFocusNode.requestFocus(),
          ),
        ),
        // DraggableScrollableSheet
        DraggableScrollableSheet(
          initialChildSize: 0.2,
          minChildSize: 0.2,
          maxChildSize: 0.75,
          controller: _dssController,
          builder: (context, scrollController) {
            return Container(
              // DSS内部内容
            );
          },
        ),
      ],
    ),
  ),
);

步骤2:监听焦点手动控制DSS状态

通过搜索栏的焦点变化,手动控制DSS的展开/收起,避免键盘触发的布局冲突:

final _dssController = DraggableScrollableController();
late FocusNode _searchFocusNode;

@override
void initState() {
  super.initState();
  _searchFocusNode = FocusNode();
  
  // 监听焦点变化同步DSS状态
  _searchFocusNode.addListener(() {
    if (_searchFocusNode.hasFocus) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        _dssController.animateTo(0.75, 
          duration: const Duration(milliseconds: 300), 
          curve: Curves.easeInOut);
      });
    } else {
      _dssController.animateTo(0.2, 
        duration: const Duration(milliseconds: 300), 
        curve: Curves.easeInOut);
    }
  });
}

@override
void dispose() {
  _searchFocusNode.dispose();
  _dssController.dispose();
  super.dispose();
}

关键原理

  • resizeToAvoidBottomInset: false 阻止Scaffold整体调整布局,但必须给Stack外层套固定尺寸的SizedBox,彻底避免Stack因父布局约束变化缩放
  • 通过焦点监听手动控制DSS状态,绕开键盘弹出时的布局计算冲突
  • 给GoogleMap设置固定尺寸容器,消除重绘导致的抖动

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:42:05