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 地图标记的主要内容,如果未能解决你的问题,请参考以下文章

如何在颤动中间接调用另一个小部件中的 ontap 函数?

如何使用插件audio_service颤动处理音频通知的onTap?

Flutter 的 onTap 方法打开存储在 Firestore 中的经纬度

如何在颤动中将图标放在自定义按钮内

如何在颤动中更改特定的图标颜色?

如何在颤动的 ReorderableListView 图标上添加颜色变化?