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

Flutter GMap点击Marker时显示自定义Widget的问题排查

问题:自定义地图Marker弹窗无法正常显示

我希望在地图上放置Marker或点击Marker时,显示一个包含经纬度信息和按钮的Widget,但以下代码无法实现预期效果,请问问题出在哪里?


处理Marker的代码

Future<void> addMarker(
    LatLng mLatLng, String mTitle, String mDescription) async {
  final Uint8List markerIcon =
      await getBytesFromAsset('assets/images/icons/pin.png', 100);
  setState(() {
    _markers.clear();
    _markers.add(Marker(
      markerId: MarkerId((mTitle + "_" + _markers.length.toString()).toString()),
      position: mLatLng,
      infoWindow: InfoWindow(
        title: mTitle,
        snippet: mDescription,
      ),
      icon: BitmapDescriptor.fromBytes(
          markerIcon), //BitmapDescriptor.defaultMarker,
    ));
  });
}

Future<void> _handleTap(LatLng tappedPoint) async {
  print(tappedPoint);
  final words = await api
      .convertTo3wa(Coordinates(tappedPoint.latitude, tappedPoint.longitude))
      .language('en')
      .execute();
  print(words.data()!.words);
  LatLng mLatLng = tappedPoint;
  String mTitle = '///${words.data()!.words}';
  String mDescription =
      'Coordinates: ${words.data()!.coordinates.lat}, ${words.data()!.coordinates.lng},\nNearest Place: ${words.data()!.nearestPlace}';

  setState(() {
    _markers.clear();
    if (words.isSuccessful()) {
      addMarker(mLatLng, mTitle, mDescription);
      getPillInfowindow(mLatLng, mTitle, mDescription);
      //_customInfoWindowController.hideInfoWindow!();
    } else {
      print(words.error());
    }
  });
}

自定义弹窗Widget代码

import 'package:flutter/material.dart';
import 'package:mymapp/screens/home_page.dart';

Widget getPillInfowindow(mLatLng, mTitle, mDescription) {
  return Positioned(
    bottom: 100, right: 0, left: 0,
    child: Align(
      alignment: Alignment.bottomCenter,
      child: Container(
        margin: EdgeInsets.all(20),
        height: 70,
        decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.all(Radius.circular(50)),
            boxShadow: <BoxShadow>[
              BoxShadow(
                  blurRadius: 20,
                  offset: Offset.zero,
                  color: Colors.grey.withOpacity(0.5))
            ]),
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Container(
                margin: EdgeInsets.only(left: 10),
                width: 50,
                height: 50,
                child: ClipOval(
                    child: Image.asset('assets/images/icons/pin2.png',
                        fit: BoxFit.cover))),
            // first widget
            Expanded(
              child: Container(
                margin: EdgeInsets.only(left: 20),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text('title:${mTitle}', //${mTitle}',
                        style: TextStyle(
                            color: Colors
                                .green)), //currentlySelectedPin.labelColor)),
                    Text(
                        'Latitude, Longitude: ${mLatLng}', 
                        style: TextStyle(fontSize: 12, color: Colors.grey)),
                    Text(
                        'Desc: ${mDescription}', // 
                        style: TextStyle(fontSize: 12, color: Colors.grey))
                  ], // end of Column Widgets
                ), // end of Column
              ),
            ), // second widget

          ],
        ),
      ),
    ),
  ); // end of Container
}

问题根源与修复方案

核心问题

  1. 自定义Widget未加入Widget树:调用getPillInfowindow()只是创建了Widget实例,但没有将其添加到页面的Widget树中,Flutter无法渲染不在树中的组件。
  2. Marker点击事件未绑定:当前代码仅处理了地图点击添加Marker的逻辑,没有监听Marker本身的点击事件,无法触发弹窗显示。
  3. 缺少状态控制:没有变量管理弹窗的显示/隐藏状态,也没有存储选中Marker的信息,无法动态更新弹窗内容。

修复步骤

1. 添加状态变量

在页面的State类中添加以下变量,用于控制弹窗状态和存储选中信息:

bool _showCustomInfoWindow = false;
LatLng? _selectedLatLng;
String? _selectedTitle;
String? _selectedDescription;

2. 为Marker绑定点击事件

修改addMarker()方法,给Marker添加onTap回调,更新状态显示弹窗:

Future<void> addMarker(
    LatLng mLatLng, String mTitle, String mDescription) async {
  final Uint8List markerIcon =
      await getBytesFromAsset('assets/images/icons/pin.png', 100);
  setState(() {
    _markers.clear();
    _markers.add(Marker(
      markerId: MarkerId((mTitle + "_" + _markers.length.toString()).toString()),
      position: mLatLng,
      infoWindow: InfoWindow(
        title: mTitle,
        snippet: mDescription,
      ),
      icon: BitmapDescriptor.fromBytes(markerIcon),
      // 绑定Marker点击事件
      onTap: () {
        setState(() {
          _showCustomInfoWindow = true;
          _selectedLatLng = mLatLng;
          _selectedTitle = mTitle;
          _selectedDescription = mDescription;
        });
      },
    ));
  });
}

3. 修改地图点击逻辑

在_handleTap()中同步更新弹窗状态:

Future<void> _handleTap(LatLng tappedPoint) async {
  // ... 原有代码保持不变
  setState(() {
    _markers.clear();
    if (words.isSuccessful()) {
      addMarker(mLatLng, mTitle, mDescription);
      // 更新弹窗状态
      _showCustomInfoWindow = true;
      _selectedLatLng = mLatLng;
      _selectedTitle = mTitle;
      _selectedDescription = mDescription;
    } else {
      print(words.error());
    }
  });
}

4. 将弹窗加入Widget树

在页面的build方法中,把弹窗作为Stack的子组件添加,根据状态控制显示:

@override
Widget build(BuildContext context) {
  return Scaffold(
    body: Stack(
      children: [
        // 你的地图组件(比如GoogleMap)
        GoogleMap(
          onTap: _handleTap,
          markers: Set<Marker>.of(_markers),
          // 其他地图配置项...
        ),
        // 根据状态显示自定义弹窗
        if (_showCustomInfoWindow && _selectedLatLng != null && _selectedTitle != null && _selectedDescription != null)
          getPillInfowindow(_selectedLatLng!, _selectedTitle!, _selectedDescription!),
      ],
    ),
  );
}

5. 可选:添加关闭按钮

给弹窗增加关闭按钮,提升交互体验:

Widget getPillInfowindow(LatLng mLatLng, String mTitle, String mDescription) {
  return Positioned(
    bottom: 100, right: 0, left: 0,
    child: Align(
      alignment: Alignment.bottomCenter,
      child: Container(
        // ... 原有代码保持不变
        child: Row(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            // ... 原有组件保持不变
            // 添加关闭按钮
            IconButton(
              icon: Icon(Icons.close, color: Colors.grey),
              onPressed: () {
                setState(() {
                  _showCustomInfoWindow = false;
                });
              },
            ),
          ],
        ),
      ),
    ),
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:56:08