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

Flutter如何实现类Google Maps列表的Draggable scrollable sheet交互

Flutter实现类Google Maps拖拽唤出滚动面板方案

Flutter 原生自带的DraggableScrollableSheet组件无需引入第三方依赖,即可实现你要的交互:初始状态仅露出底部一小段容器,上滑拖拽任意位置(包括容器内的列表项)即可跟随手势展开面板,支持多档位吸附、和列表滚动自动联动,完全匹配Google Maps的交互逻辑。

效果参考

  • 初始折叠状态:示例图1
  • 上滑半展开状态:示例图2
  • 完全展开状态:Draggable scrollable sheet参考效果图

核心实现代码

页面采用Stack层叠布局,底层放置地图/主内容区,顶层放置DraggableScrollableSheet即可,完整可运行示例:

import 'package:flutter/material.dart';

class GoogleMapStyleSheetPage extends StatelessWidget {
  const GoogleMapStyleSheetPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Stack(
        children: [
          // 底层主内容(这里替换成你的Google Maps/其他地图组件即可)
          Container(color: Colors.lightBlue[50], child: const Center(child: Text("地图区域"))),
          // 顶层拖拽面板
          DraggableScrollableSheet(
            // 初始高度:仅露出底部12%高度的容器作为拖拽入口
            initialChildSize: 0.12,
            // 最小折叠高度:和初始值一致
            minChildSize: 0.12,
            // 最大展开高度:占屏幕90%,留顶部状态栏空间
            maxChildSize: 0.9,
            // 开启档位吸附
            snap: true,
            // 三个吸附档位:折叠、半开、全开
            snapSizes: const [0.12, 0.5, 0.9],
            builder: (context, scrollController) {
              return Container(
                decoration: const BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
                  boxShadow: [BoxShadow(blurRadius: 12, color: Colors.black12)],
                ),
                child: Column(
                  children: [
                    // 顶部拖拽把手
                    Container(
                      margin: const EdgeInsets.symmetric(vertical: 12),
                      width: 40,
                      height: 4,
                      decoration: BoxDecoration(
                        color: Colors.grey[350],
                        borderRadius: BorderRadius.circular(2),
                      ),
                    ),
                    // 联动列表:必须传入builder提供的scrollController
                    Expanded(
                      child: ListView.builder(
                        controller: scrollController,
                        padding: EdgeInsets.zero,
                        itemCount: 30,
                        itemBuilder: (context, index) {
                          return ListTile(
                            leading: const Icon(Icons.location_on),
                            title: Text("地点条目 ${index + 1}"),
                            subtitle: Text("这是第${index + 1}个地点的描述信息"),
                          );
                        },
                      ),
                    ),
                  ],
                ),
              );
            },
          ),
        ],
      ),
    );
  }
}

关键注意点

  • 手势联动无需额外手写监听:只要你把列表组件的controller属性赋值为DraggableScrollableSheet.builder回调提供的scrollController,组件会自动处理手势优先级:面板未完全展开时,垂直拖拽优先触发面板展开/折叠;面板完全展开后,垂直拖拽优先触发列表滚动,拖拽列表项时也能直接唤出面板,和Google Maps逻辑完全一致。
  • 如需主动控制面板展开/折叠,可以通过GlobalKey获取组件状态调用动画方法:
    // 声明key
    final sheetKey = GlobalKey<DraggableScrollableSheetState>();
    // 绑定到DraggableScrollableSheet的key属性
    // 主动展开到半开状态示例
    sheetKey.currentState?.animateTo(
      0.5,
      duration: const Duration(milliseconds: 300),
      curve: Curves.easeOutCubic,
    );
    
  • 如果需要调整面板跟手度,可以修改组件的snapAnimationDuration参数调整吸附动画时长,日常开发设200-300ms体验最佳。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 20:42:19