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 }
问题根源与修复方案
核心问题
- 自定义Widget未加入Widget树:调用
getPillInfowindow()只是创建了Widget实例,但没有将其添加到页面的Widget树中,Flutter无法渲染不在树中的组件。 - Marker点击事件未绑定:当前代码仅处理了地图点击添加Marker的逻辑,没有监听Marker本身的点击事件,无法触发弹窗显示。
- 缺少状态控制:没有变量管理弹窗的显示/隐藏状态,也没有存储选中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
相关产品推荐
相关产品推荐

