Skip to content

Google Maps onCameraIdle not working in iOS when in a Stack. #33988

Closed

Description

@leen-nobbas

Steps to Reproduce

I have a layout where in a Stack I have a GoogleMap, a GestureDetector on top of it that currently absorbs all the taps and doesn't pass it to the GoogleMap. Once tapped, GestureDetector allows the taps to be passed onto GoogleMap.
Problem is, whenever I make this kind of layout, the onCameraIdle callback is not being called in iOS (works in Android). Double tapping the map or tapping on a marker somehow resets the callback, but it doesn't work the first time.

class FancyMapPageState extends State<FancyMapPage> {

  ...

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        GoogleMap(
        ...
        ),
        GestureDetector(
          behavior: !_animationController.isCompleted
              ? HitTestBehavior.opaque
              : HitTestBehavior.deferToChild,
          onTap: () {
            _animationController.forward();
          },
        ),
        SlideTransition(
          position: _cardSlideAnimation,
          Container(
            child: ...,
          ),
        ),
      ],
    );
  }
}

Logs

No issues found! (ran in 5.9s)
[✓] Flutter (Channel stable, v1.5.4-hotfix.2, on Mac OS X 10.14.5 18F132, locale en-IN)
    • Flutter version 1.5.4-hotfix.2 at /Users/gurleensethi/Documents/Flutter/flutter
    • Framework revision 7a4c33425d (5 weeks ago), 2019-04-29 11:05:24 -0700
    • Engine revision 52c7a1e849
    • Dart version 2.3.0 (build 2.3.0-dev.0.5 a1668566e5)

 
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
    • Android SDK at /Users/gurleensethi/Library/Android/sdk
    • Android NDK location not configured (optional; useful for native profiling support)
    • Platform android-28, build-tools 28.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)
    • All Android licenses accepted.

[✓] iOS toolchain - develop for iOS devices (Xcode 10.2.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 10.2.1, Build version 10E1001
    • ios-deploy 1.9.4
    • CocoaPods version 1.5.3

[✓] Android Studio (version 3.4)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin version 35.3.1
    • Dart plugin version 183.6270
    • Java version OpenJDK Runtime Environment (build 1.8.0_152-release-1343-b01)

[✓] IntelliJ IDEA Community Edition (version 2019.1.3)
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 36.0.4
    • Dart plugin version 191.7830

[✓] VS Code (version 1.35.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.1.0

[✓] Connected device (1 available)
    • Gurleen’s iPhone • 2e3ceb53501346b6a4d1b2e0ac2abf9850737cc5 • ios • iOS 12.3.1

• No issues found!

@iskakaushik Could you please look into this, thanks in advance :)

Edit by cyanglaz:
internal b/144584399

Activity

  1. leen-nobbas commented on Jun 6, 2019

    @leen-nobbas
    Author

    A complete sample example:

    import 'package:flutter/material.dart';
    import 'package:google_maps_flutter/google_maps_flutter.dart';
    
    void main() {
      runApp(App());
    }
    
    class App extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: "Maps Test",
          home: MapTestWidget(),
        );
      }
    }
    
    class MapTestWidget extends StatefulWidget {
      @override
      _MapTestWidgetState createState() => _MapTestWidgetState();
    }
    
    class _MapTestWidgetState extends State<MapTestWidget> {
      bool _isOpen = true;
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Stack(
            children: <Widget>[
              _buildMap(),
              _buildTranslucent(),
              _buildSampleCard(),
            ],
          ),
        );
      }
    
      Widget _buildSampleCard() {
        return Positioned(
          bottom: 0.0,
          right: 0.0,
          left: 0.0,
          child: Transform.translate(
            offset: _isOpen ? Offset.zero : Offset(0.0, 400.0),
            child: Container(
              height: 400.0,
              color: Colors.red,
            ),
          ),
        );
      }
    
      Widget _buildTranslucent() {
        return GestureDetector(
          behavior: _isOpen ? HitTestBehavior.opaque : HitTestBehavior.deferToChild,
          onTap: () {
            setState(() {
              _isOpen = false;
            });
          },
          child: Container(),
        );
      }
    
      Widget _buildMap() {
        return GoogleMap(
          initialCameraPosition: CameraPosition(
            target: LatLng(25.7617, -80.1918),
            zoom: 16.0,
          ),
          //markers: _markers.values.toSet(),
          onCameraIdle: () {
            print("Camera Idle1");
            print("Camera Idle2");
          },
          onMapCreated: (controller) {},
        );
      }
    }
    
  2. darwin-morocho commented on Jun 29, 2019

    @darwin-morocho

    I have the same problem

  3. ludowkm commented on Jul 4, 2019

    @ludowkm

    I also met this problem, any suggestion ?

  4. droidpl commented on Jul 27, 2019

    @droidpl

    Same problem here. This is critical to do thing on camera movement that require an await, as there is no other callback to do this

  5. ludowkm commented on Jul 29, 2019

    @ludowkm

    I overcome it by a workaround, on onCameraMove, i make a delay subscription 150 milliseconds then the result will be onCameraIdle, but before subscribe it, do cancelling first because onCameraMove will be call multiple times before. Like the below:
    onCameraMove: (newPosition) { _mapIdleSubscription?.cancel(); _mapIdleSubscription = Future.delayed(Duration(milliseconds: 150)) .asStream() .listen((_) => _onCameraIdle()); }

  6. papalardo commented on Aug 21, 2019

    @papalardo

    I have the same problem, but when I move List on the Map

  7. DennisKragekjaer commented on Aug 26, 2019

    @DennisKragekjaer

    Same issue here. Its becoming worrying how many problems there is with google maps/ platform views.
    I have same structure as you, with a button on top of Google maps. When I press the button everything related to map stops working, no onIdle and so on. When I tap a marker or double tap the maps, it is working again, until I press the button.

  8. leen-nobbas commented on Aug 26, 2019

    @leen-nobbas
    Author

    @justinmc Can we have this on priority?

  9. papalardo commented on Aug 26, 2019

    @papalardo

    I overcome it by a workaround, on onCameraMove, i make a delay subscription 150 milliseconds then the result will be onCameraIdle, but before subscribe it, do cancelling first because onCameraMove will be call multiple times before. Like the below:
    onCameraMove: (newPosition) { _mapIdleSubscription?.cancel(); _mapIdleSubscription = Future.delayed(Duration(milliseconds: 150)) .asStream() .listen((_) => _onCameraIdle()); }

    alright that's palliative

  10. justinmc commented on Aug 27, 2019

    @justinmc
    Contributor
  11. idompolo commented on Aug 27, 2019

    @idompolo

    Update the camera using mapController for button click event.

    example)

    onTap: () {
    setState(() {
    _isOpen = false;
    });
    mapController.animateCamera(...)
    },

    Camera update, for example, does not change the actual screen if the zoom value is equal to the existing value.

  12. dipendra-sharma commented on Dec 11, 2019

    @dipendra-sharma

    Any solution for this?

  13. 15 remaining items

  14. goderbauer commented on Feb 19, 2020

    @goderbauer
    Member

    Looks like there's no b/ bug attached to this. Removing customer: google and critical labels.

  15. cyanglaz commented on Feb 19, 2020

    @cyanglaz

    @goderbauer I am going to attaching the b/ bug and relabel it.

  16. TimG38 commented on Mar 18, 2020

    @TimG38

    If it's any help, here's some additional information on how to reproduce it:

    google_maps_flutter: ^0.5.24+1

    class _MyHomePageState extends State<MyHomePage> {
      GoogleMapController mapController;
    
      final LatLng _center = const LatLng(45.521563, -122.677433);
    
      void _onMapCreated(GoogleMapController controller) {
        mapController = controller;
      }
    
      bool isCameraMoving;
    
      @override
      void initState() {
        super.initState();
        isCameraMoving = false;
      }
    
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          home: Scaffold(
            appBar: AppBar(
              title: Text('Maps POC App'),
            ),
            body: Stack(
              children: <Widget>[
                GoogleMap(
                  onMapCreated: _onMapCreated,
                  initialCameraPosition: CameraPosition(
                    target: _center,
                    zoom: 11.0,
                  ),
                  onCameraMove: (CameraPosition cameraPosition) {
                    setState(() {
                      isCameraMoving = true;
                    });
                  },
                  onCameraIdle: () {
                    setState(() {
                      isCameraMoving = false;
                    });
                  },
                ),
                Positioned(
                  top: 0,
                  left: 0,
                  right: 0,
                  child: Padding(
                    padding: const EdgeInsets.all(24.0),
                    child: Container(
                      alignment: Alignment.center,
                      color: Colors.cyan,
                      height: 200,
                      width: MediaQuery.of(context).size.width,
                      child: Text(
                        'Camera moving: $isCameraMoving',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 24, color: Colors.white),
                      ),
                    ),
                  ),
                )
              ],
            ),
          ),
        );
      }
    }
    
    1. Move the map location, bool value keeps changing.
    2. Tap on the container anywhere.
    3. Now again move the map.
    4. bool value does not change.(onCameraIdle() not working).
  17. masewo commented on Apr 13, 2020

    @masewo

    @cyanglaz Can you please push your solution to a branch so that we do not have to wait for the engine fix to hit stable?

  18. asksage commented on Apr 25, 2020

    @asksage

    I just wrote a CustomPanGestureDetector that doesn't fully consume events (tested on iOS Simulator). I hope this helps:

    https://github.com/asksage/rawFiles/blob/master/custom_pan_detector.dart

    Wrap your GoogleMap widget like this:

    RawGestureDetector( gestures: <Type, GestureRecognizerFactory>{ CustomPanGestureDetector: GestureRecognizerFactoryWithHandlers<CustomPanGestureDetector>( () => CustomPanGestureDetector( onPanStart: _onDown, onPanEnd: _onUp, ), (CustomPanGestureDetector instance) {}, ), }, child: GoogleMap

  19. asksage commented on Apr 25, 2020

    @asksage

    I just wrote a CustomPanGestureDetector that doesn't fully consume events (tested on iOS Simulator). I hope this helps:

    https://github.com/asksage/rawFiles/blob/master/custom_pan_detector.dart

    Wrap your GoogleMap widget like this:

    RawGestureDetector( gestures: <Type, GestureRecognizerFactory>{ CustomPanGestureDetector: GestureRecognizerFactoryWithHandlers<CustomPanGestureDetector>( () => CustomPanGestureDetector( onPanStart: _onDown, onPanEnd: _onUp, ), (CustomPanGestureDetector instance) {}, ), }, child: GoogleMap

    Please, note I am not returning event.position in the callbacks (so add that if you need it) because I just wrote this to test the concept.

  20. MarcinusX commented on Apr 30, 2020

    @MarcinusX
    Contributor

    @asksage solution didn't solve the problem for me.

    I've put @TimG38 code to gist: https://gist.github.com/MarcinusX/97230719f58e6835b0761f369d88b608
    The bug still occurs on version 0.5.27. Tested on simulator iPhone11

    ezgif com-video-to-gif (13)

  21. baldwizard commented on May 5, 2020

    @baldwizard

    I stumbled on this issue today. I thought about a simple workaround that might be helpful in some cases (although i am sure it doesn't solve every case ... ). The idea is rather simple - keep track of the last position to which camera got moved :

    • onCameraMove: (CameraPosition position) {lastLatLng = position.target;},
    • and after clicking some button located on the Stack widget -> execute: _controller.moveCamera(CameraUpdate.newLatLng(lastLatLng))
    • this will "move" camera to the last position and also issue onCameraMoveStarted, onCameraMove and onCameraMoveIdle events.

    The good thing is that it will reset reading events by google maps widget back to normal the bad side is that it will create few unwanted events.

  22. baldwizard commented on May 6, 2020

    @baldwizard

    Btw. you can restrict issuing .moveCamera events to minimum by checking if onCameraMove event ended with onCameraIdle event:

    • onCameraMove: (CameraPosition position) {lastLatLng = position.target; isMoveTerimanted = false},
    • onCameraIdle: () => isMoveTerminated = true
    • onTap (in some button on Stack) : () { if(!isMoveTerminated) _controller.moveCamera(CameraUpdate.newLatLng(lastLatLng)) }

    It should issue moveCamera events only when onCameraMove event doesn't get terminated with onCameraIdle event.

    Update: it seems that it's just safer to issue .moveCamera events every time button is clicked (restricting them will prevent this workaround from working in some cases)

  23. cyanglaz commented on May 12, 2020

    @cyanglaz

    This issue has been fixed in flutter-team-archive/plugins#2746
    Please update the plugin to 0.5.27+3 to apply the fix.
    I'm closing the issue.

  24. github-actions commented on Aug 21, 2021

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  25. locked as resolved and limited conversation to collaborators on Aug 21, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

a: platform-viewsEmbedding Android/iOS views in Flutter appscustomer: googleVarious Google teamsp: mapsGoogle Maps pluginpackageflutter/packages repository. See also p: labels.platform-iosiOS applications specifically

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions