You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Flutter中点击Google Map Marker后显示Bottom Sheet的实现方法

实现Flutter Google Map Marker点击显示Bottom Sheet

嘿,这个需求我之前做过好多次,实现起来思路很清晰,我给你拆解成具体步骤,附上完整代码示例:

1. 先搞定依赖

首先确保你的pubspec.yaml里引入了google_maps_flutter(毕竟要用到地图),如果还没加的话,加上这行:

dependencies:
  flutter:
    sdk: flutter
  google_maps_flutter: ^2.5.0 # 换成最新版本就行

然后跑一下flutter pub get拉取依赖。

2. 初始化Google Map与Marker

先创建一个StatefulWidget,因为我们需要管理地图控制器和Marker的状态。核心是给每个Marker绑定onTap回调,这是触发Bottom Sheet的入口。

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

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

  @override
  State<MapWithMarkerBottomSheet> createState() => _MapWithMarkerBottomSheetState();
}

class _MapWithMarkerBottomSheetState extends State<MapWithMarkerBottomSheet> {
  late GoogleMapController _mapController;
  // 模拟的地点数据,你可以换成自己的数据源
  final List<Map<String, dynamic>> _places = [
    {
      'id': '1',
      'name': '中央公园',
      'latLng': const LatLng(40.7829, -73.9654),
      'description': '纽约最著名的城市公园,占地843英亩',
      'address': '纽约市曼哈顿区'
    },
    {
      'id': '2',
      'name': '帝国大厦',
      'latLng': const LatLng(40.7484, -73.9857),
      'description': '标志性摩天大楼,曾是世界最高建筑',
      'address': '纽约市曼哈顿第五大道350号'
    }
  ];

  // 生成Marker集合
  Set<Marker> _buildMarkers() {
    return _places.map((place) {
      return Marker(
        markerId: MarkerId(place['id']),
        position: place['latLng'],
        infoWindow: InfoWindow(title: place['name']), // 可选:默认的InfoWindow可留可删
        onTap: () => _showPlaceBottomSheet(place), // 关键:点击触发Bottom Sheet
      );
    }).toSet();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('地图地点详情')),
      body: GoogleMap(
        initialCameraPosition: const CameraPosition(
          target: LatLng(40.7128, -74.0060), // 纽约坐标作为初始位置
          zoom: 12,
        ),
        onMapCreated: (controller) => _mapController = controller,
        markers: _buildMarkers(),
      ),
    );
  }

  @override
  void dispose() {
    _mapController.dispose(); // 记得释放控制器
    super.dispose();
  }
}

3. 实现Bottom Sheet弹窗逻辑

接下来写_showPlaceBottomSheet方法,用Flutter自带的showModalBottomSheet构建弹窗,里面可以自定义任意你需要的详情UI:

void _showPlaceBottomSheet(Map<String, dynamic> place) {
  showModalBottomSheet(
    context: context,
    isScrollControlled: true, // 可选:内容太长时允许滚动并适配屏幕高度
    shape: const RoundedRectangleBorder(
      borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
    ),
    builder: (context) {
      return Padding(
        padding: EdgeInsets.only(
          bottom: MediaQuery.of(context).viewInsets.bottom, // 适配键盘(如果有输入框的话)
        ),
        child: Container(
          padding: const EdgeInsets.all(16),
          child: Column(
            mainAxisSize: MainAxisSize.min, // 让高度自适应内容
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // 顶部标题栏,可选:加个关闭按钮
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Text(
                    place['name'],
                    style: const TextStyle(
                      fontSize: 20,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  IconButton(
                    icon: const Icon(Icons.close),
                    onPressed: () => Navigator.pop(context),
                  ),
                ],
              ),
              const SizedBox(height: 12),
              // 地点地址
              Text(
                '地址:${place['address']}',
                style: const TextStyle(color: Colors.grey),
              ),
              const SizedBox(height: 12),
              // 地点描述
              Text(place['description']),
              const SizedBox(height: 20),
              // 可以加更多功能按钮,比如导航、分享
              Row(
                children: [
                  Expanded(
                    child: ElevatedButton(
                      onPressed: () {
                        // 这里可以加导航逻辑,比如调用第三方地图应用
                        Navigator.pop(context);
                      },
                      child: const Text('导航'),
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: OutlinedButton(
                      onPressed: () {
                        // 分享逻辑实现
                        Navigator.pop(context);
                      },
                      child: const Text('分享'),
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      );
    },
  );
}

几个实用小技巧

  • 控制Bottom Sheet高度:如果不想让弹窗占满屏幕,把isScrollControlled设为false,或者给Container指定固定高度。
  • 异步数据加载:如果地点详情需要从网络获取,在_showPlaceBottomSheet里先显示加载状态(比如CircularProgressIndicator),请求完成后再渲染详情UI。
  • Marker交互增强:点击Marker时,可以给Marker加缩放或颜色变化的动画,比如修改Marker的icon属性,提升用户体验。
  • 多屏幕适配:用MediaQuery调整弹窗的padding和大小,确保在手机和平板上都有良好的显示效果。

这样一套下来,点击地图上的Marker就能弹出漂亮的Bottom Sheet展示地点详情啦~

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

火山引擎 最新活动