Ontap 在颤动中无法使用 Google 地图标记
Posted
技术标签:
【中文标题】Ontap 在颤动中无法使用 Google 地图标记【英文标题】:Ontap is not working on Google Maps marker in flutter 【发布时间】:2020-07-20 23:13:51 【问题描述】:我正在做一个项目,我正在使用 google_maps_flutter 在屏幕上显示地图和标记,并使用 search_map_place 在地图上搜索地点,我已经提供了来源下面的代码,源代码中有一个错误,整个项目是通过底部导航控制的,当我第一次运行应用程序时,我导航到地图屏幕,它显示来自数据库的标记,onTap 也可以正常工作,但是当我导航时到任何其他屏幕并再次导航回地图屏幕,onTap 对标记不起作用。
在地图上显示标记:
_onMapCreated() async
// print(customerInfo);
for (var item in ud.customerInfo)
if (item['customer_latitude'] == "" && item['customer_longitude'] == "")
continue;
else
final MarkerId markerId = MarkerId((markers.length + 1).toString());
LatLng markerPos = LatLng(double.parse(item['customer_latitude']),
double.parse(item['customer_longitude']));
final Marker marker = Marker(
infoWindow: InfoWindow(
title: item['customer_name'],
snippet: item['customer_desc'],
),
// onTap: () => _onMarkerTapped(markerId),
icon: BitmapDescriptor.defaultMarkerWithHue(
item['customer_status'] == 'red'
? BitmapDescriptor.hueRed
: item['customer_status'] == 'yellow'
? BitmapDescriptor.hueYellow
: BitmapDescriptor.hueGreen),
markerId: markerId,
consumeTapEvents: true,
position: markerPos,
onTap: ()
Navigator.push(
context,
MaterialPageRoute<Null>(
builder: (BuildContext context)
return CustomerDetail(
item['customer_id'],
item['customer_name'],
item['customer_email'],
item['customer_desc'],
item['customer_phone'],
item['customer_status']);
,
fullscreenDialog: true,
));
);
_markers = null;
markers[markerId] = marker;
setState(() );
完整代码:
String _mapStyle;
GoogleMapController controller;
List<Map> customerInfo;
class MapS extends StatefulWidget
@override
_MapSState createState() => _MapSState();
class _MapSState extends State<MapS>
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Search Map Place Demo',
home: MainScreen(),
);
MapType _mapType = MapType.normal;
Map<MarkerId, Marker> markers = <MarkerId, Marker>;
class MapSample extends StatefulWidget
@override
State<MapSample> createState() => MapSampleState();
class MapSampleState extends State<MapSample>
@override
void initState()
super.initState();
getCustomerInfo();
rootBundle.loadString('assets/map_style.txt').then((string)
_mapStyle = string;
);
getCurrentLocation();
setCustomMapPin();
// _onMapCreated(controller);
setState(()
_onMapCreated();
);
Future getCustomerInfo() async
// getting customer info using saleManagerID
var customerInfoUrl = "http://.../calender/getCustomerInfo.php";
customerInfoResponse = await http.post(customerInfoUrl, body:
"sm_id": sm.sm_id,
);
if (jsonDecode(customerInfoResponse.body) == "No Customers")
String noCustomers = jsonDecode(customerInfoResponse.body);
ud.customerInfo = [];
else
ud.customerInfo = jsonDecode(customerInfoResponse.body);
print('user data got!');
// print(ud.customerInfo);
Widget _mapTypeSwitcher() => Align(
alignment: const Alignment(0.95, -0.7),
child: FloatingActionButton(
mini: true,
heroTag: null,
tooltip: 'Map Type',
child: const Icon(Icons.layers),
onPressed: ()
final MapType nextType =
MapType.values[_mapType.index == 2 ? 1 : 2];
setState(()
print(nextType.toString());
_mapType = nextType;
);
,
),
);
_onMapCreated() async
// print(customerInfo);
for (var item in ud.customerInfo)
if (item['customer_latitude'] == "" && item['customer_longitude'] == "")
continue;
else
final MarkerId markerId = MarkerId((markers.length + 1).toString());
LatLng markerPos = LatLng(double.parse(item['customer_latitude']),
double.parse(item['customer_longitude']));
final Marker marker = Marker(
infoWindow: InfoWindow(
title: item['customer_name'],
snippet: item['customer_desc'],
),
// onTap: () => _onMarkerTapped(markerId),
icon: BitmapDescriptor.defaultMarkerWithHue(
item['customer_status'] == 'red'
? BitmapDescriptor.hueRed
: item['customer_status'] == 'yellow'
? BitmapDescriptor.hueYellow
: BitmapDescriptor.hueGreen),
markerId: markerId,
consumeTapEvents: true,
position: markerPos,
onTap: ()
Navigator.push(
context,
MaterialPageRoute<Null>(
builder: (BuildContext context)
return CustomerDetail(
item['customer_id'],
item['customer_name'],
item['customer_email'],
item['customer_desc'],
item['customer_phone'],
item['customer_status']);
,
fullscreenDialog: true,
));
);
_markers = null;
markers[markerId] = marker;
setState(() );
void getCurrentLocation() async
Position res = await Geolocator()
.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
setState(()
position = res;
// _child = mapWidget();
print(res);
mapToggle = true;
);
Completer<GoogleMapController> mapController = Completer();
Set<Marker> _markers = Set();
// custom marker
BitmapDescriptor pinLocationIcon;
void setCustomMapPin() async
pinLocationIcon = await BitmapDescriptor.fromAssetImage(
ImageConfiguration(devicePixelRatio: 4.5), 'assets/red_marker.png');
IconData fontAwesomeIconFromString(String name)
switch (name)
case 'website':
return Icons.web;
case 'phone_no':
return Icons.phone_android;
case 'desc':
return Icons.lock_outline;
case 'place':
return Icons.place;
case 'city':
return Icons.location_city;
case 'country':
return Icons.my_location;
String iconfromApi;
// textfield in bottomsheet
Widget _entryField(String title, TextEditingController tcont, String ic)
iconfromApi = ic;
return Container(
margin: EdgeInsets.symmetric(vertical: 5),
padding: new EdgeInsets.fromLTRB(2.0, 2.0, 2.0, 2.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
TextFormField(
controller: tcont,
// obscureText: isPassword,
style: TextStyle(color: Colors.black, fontFamily: 'Montserrat'),
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: title,
prefixIcon: Icon(fontAwesomeIconFromString(iconfromApi)),
labelStyle: TextStyle(fontSize: 15)),
)
],
),
);
Future addData(String phone, web) async
var url = "http://.../addData.php";
var response = await http.post(url, body:
"name": userD.name,
"desc": userD.desc,
"status": userD.status,
"latitude": userD.latitide,
"longitude": userD.longitude,
"phone": phone != null ? phone : "n/a",
"web": web != null ? web : "n/a",
"sm_id": sm.sm_id
);
// print('Response status: $response.statusCode');
// print('Response body: $response.body');
response.body == '1' ? confirmDialog(context) : errorDialog(context);
Future<void> confirmDialog(BuildContext context)
return showDialog<void>(
context: context,
barrierDismissible: false,
builder: (BuildContext context)
return AlertDialog(
title: Text('Response'),
content: const Text('Record Added!'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: ()
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
(Route<dynamic> route) => false);
,
),
],
);
,
);
Future<void> errorDialog(BuildContext context)
return showDialog<void>(
barrierDismissible: false,
context: context,
builder: (BuildContext context)
return AlertDialog(
title: Text('Response'),
content: const Text('Record submission failed'),
actions: <Widget>[
FlatButton(
child: Text('Ok'),
onPressed: ()
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(builder: (context) => MainScreen()),
(Route<dynamic> route) => false);
,
),
],
);
,
);
Widget _submitButton()
return InkWell(
onTap: ()
print(ud.phone);
print(ud.web);
addData(ud.phone, ud.web);
,
child: Container(
width: MediaQuery.of(context).size.width,
padding: EdgeInsets.symmetric(vertical: 15),
alignment: Alignment.center,
decoration: BoxDecoration(
borderRadius: BorderRadius.all(Radius.circular(5)),
gradient: LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Hexcolor("#2a7837"), Hexcolor("#2a7837")])),
child: Text(
'Save In Database',
style: TextStyle(
fontSize: 15,
color: Colors.white,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
),
);
Widget addButon = Container();
@override
Widget build(BuildContext context)
final height = MediaQuery.of(context).size.height;
return Stack(
children: <Widget>[
GoogleMap(
myLocationEnabled: true,
myLocationButtonEnabled: true,
compassEnabled: false,
tiltGesturesEnabled: false,
mapType: _mapType,
initialCameraPosition: CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 15),
onMapCreated: (controller)
mapController.complete(controller);
_onMapCreated();
// add things here on map, markers, design etc
controller.setMapStyle(_mapStyle);
,
markers:
_markers == null ? Set<Marker>.of(markers.values) : _markers
// markers: Set<Marker>.of(markers.values),
),
_mapTypeSwitcher(),
Positioned(
top: 60,
left: MediaQuery.of(context).size.width * 0.05,
// width: MediaQuery.of(context).size.width * 0.9,
child: SearchMapPlaceWidget(
apiKey: apiKEY,
location: CameraPosition(
target: LatLng(position.latitude, position.longitude),
zoom: 18)
.target,
radius: 30000,
onSelected: (place) async
// declaring some variables
final geolocation = await place.geolocation;
// final js = geolocation.fullJSON;
controller = await mapController.future;
// get latLong in a
var latLong = geolocation.coordinates;
// to save latLong in array for db
var latLongList = new List(2);
// to save place, city & country in array for db
var loctionList = new List();
controller.animateCamera(
CameraUpdate.newLatLng(geolocation.coordinates));
controller.animateCamera(
CameraUpdate.newLatLngBounds(geolocation.bounds, 0));
print(place.fullJSON);
// converting LatLong to string and then array
latLong = latLong.toString().replaceAll(RegExp("[a-zA-Z]"), '');
latLong =
latLong.toString().replaceAll(RegExp("[\\[\\]()]"), '');
ud.customerDetials = place.fullJSON['place_id'];
latLongList = latLong.split(', ');
print(latLongList[0]);
print(latLongList[1]);
// print(prettyJson(place.fullJSON, indent: 2));
// print(a.runtimeType);
// adding retrived values to object
userD.name = place.fullJSON['structured_formatting']['main_text'];
// place.fullJSON['structured_formatting']['secondary_text'] == null
// ? userD.desc = c_desc.text
// : userD.desc =
// place.fullJSON['structured_formatting']['secondary_text'];
// c_phone_no.text = c_place.text =
// c_city.text = c_country.text = c_desc.text = '';
// userD.place = c_place.text;
// userD.city = c_city.text;
// userD.country = c_country.text;
userD.phone = ud.phone;
userD.web = ud.web;
userD.latitide = latLongList[0];
userD.longitude = latLongList[1];
userD.status = "red";
setState(()
markers = <MarkerId, Marker>;
_markers = new Set();
_markers.add(
Marker(
markerId: MarkerId(place.fullJSON['place_id']),
position: geolocation.coordinates,
icon: pinLocationIcon,
infoWindow: InfoWindow(
title: place.fullJSON['structured_formatting']
['main_text'],
snippet: place.fullJSON['structured_formatting']
['secondary_text'])),
);
// markers = null;
);
// print(js);
// savejson(js);
addButon = new RawMaterialButton(
onPressed: () async
var responseDetails =
await getLocationDetails(place.fullJSON['place_id']);
ud.phone =
responseDetails.data["result"]["formatted_phone_number"];
ud.web = responseDetails.data["result"]["website"];
showStopper(
context: context,
stops: [0.5 * height, height],
builder:
(context, scrollController, scrollPhysics, stop)
return Padding(
padding: const EdgeInsets.all(18.0),
child: ListView(
controller: scrollController,
physics: scrollPhysics,
children: [
// show location name
Text(
userD.name,
style: TextStyle(
color: Colors.black,
fontSize: 30,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
SizedBox(
height: 50,
),
// location desc
Text(
'Description:',
style: TextStyle(
color: Colors.black,
fontSize: 20,
fontWeight: FontWeight.bold,
fontFamily: 'Montserrat'),
),
SizedBox(
height: 10,
),
userD.desc == null
? _entryField("Description", c_desc, 'desc')
: Text(
userD.desc,
style: TextStyle(
color: Colors.black,
fontSize: 15,
fontFamily: 'Montserrat'),
),
SizedBox(
height: 20,
),
// add website, phone, place, city, country
Text(
ud.phone != null ? ud.phone : "phone: n/a"),
Text(ud.web != null ? ud.web : "website: n/a"),
// _userInfoWidget(),
// _entryField("Website", c_websitee, 'website'),
SizedBox(
height: 20,
),
_submitButton()
]),
);
);
,
child: new Icon(
Icons.add_location,
color: Hexcolor('#2a7837'),
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
);
,
),
),
// 2 floating buttons
Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
addButon,
new RawMaterialButton(
onPressed: ()
// Navigator.push(
// context,
// MaterialPageRoute<Null>(
// builder: (BuildContext context)
// return MyApp();
// ,
// fullscreenDialog: true,
// ));
_markers = null;
_onMapCreated();
,
child: new Icon(
Icons.person,
color: Hexcolor('#2a7837'),
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
),
// feedback button
new RawMaterialButton(
onPressed: ()
Navigator.push(
context,
MaterialPageRoute<Null>(
builder: (BuildContext context)
return FeedBackModal();
,
fullscreenDialog: true,
));
,
child: new Icon(
Icons.feedback,
color: Hexcolor('#2a7837'),
size: 35.0,
),
shape: new CircleBorder(),
elevation: 2.0,
fillColor: Colors.white,
padding: const EdgeInsets.all(15.0),
),
],
)
],
),
)
],
);
Future getLocationDetails(placeId) async
try
String requestUrl =
"https://maps.googleapis.com/maps/api/place/details/json?place_id=$placeId&fields=website,formatted_phone_number&key=$apiKEY";
Response placeDetails = await Dio().get(requestUrl);
return placeDetails;
catch (e)
print(e);
// ud.phone= placeDetails.data["result"]["formatted_phone_number"];
// ud.web= placeDetails.data["result"]["web"];
class MainScreen extends StatefulWidget
@override
_MainScreenState createState() => _MainScreenState();
class _MainScreenState extends State<MainScreen>
// bottom navigation variables
int _selectedIndex = 0;
static const TextStyle optionStyle =
TextStyle(fontSize: 30, fontWeight: FontWeight.bold);
List<Widget> _widgetOptions = <Widget>[
Kpi(),
Customers(),
MapSample(),
AddMeeting(),
Customers(),
];
void _onItemTapped(int index)
setState(()
_selectedIndex = index;
fd.indexForFeedback = _selectedIndex;
);
@override
Widget build(BuildContext context)
return Scaffold(
body: Center(
child: _widgetOptions.elementAt(fd.indexForFeedback),
),
bottomNavigationBar: BottomNavigationBar(
unselectedItemColor: Colors.white,
type: BottomNavigationBarType.fixed,
backgroundColor: Colors.black,
items: const <BottomNavigationBarItem>[
BottomNavigationBarItem(
icon: Icon(Icons.insert_chart),
title: Text('KPI'),
),
BottomNavigationBarItem(
icon: Icon(Icons.people),
title: Text('Customers'),
),
BottomNavigationBarItem(
icon: Icon(Icons.add_location),
title: Text('Map'),
),
BottomNavigationBarItem(
icon: Icon(Icons.calendar_today),
title: Text('Calender'),
),
BottomNavigationBarItem(
icon: Icon(Icons.group_add),
title: Text('Quotes'),
),
],
currentIndex: fd.indexForFeedback,
selectedItemColor: Hexcolor('#2a7837'),
onTap: _onItemTapped,
),
);
【问题讨论】:
【参考方案1】:替换
child: _widgetOptions.elementAt(fd.indexForFeedback),
有
child: IndexedStack(
children: _widgetOptions,
index: fd.indexForFeedback,
),
它可以防止每次在选项卡之间切换时重绘地图,并避免内存泄漏。
另请参阅:https://www.youtube.com/watch?v=_O0PPD1Xfbk
【讨论】:
应用您的解决方案后,现在它给了我异常,现在该怎么办?以上是关于Ontap 在颤动中无法使用 Google 地图标记的主要内容,如果未能解决你的问题,请参考以下文章
如何使用插件audio_service颤动处理音频通知的onTap?