Flutter 지도에서 애니메이션 및 대화형 사용자 지정 지도 마커를 추가하는 방법

지도에서 마커는 정확한 위도 및 경도 좌표로 위치를 나타내는 데 사용되는 기본 도구입니다. SyncfusionFlutter Maps 위젯에는 MapShapeLayer 또는 MapTileLayer에 마커를 추가하는 지원 기능이 내장되어 있습니다. 모든 유형의 사용자 정의 위젯을 마커로 사용하거나 원, 다이아몬드, 사각형 및 삼각형과 같은 기본 제공 모양을 사용할 수 있습니다.

이 블로그에서는 마커를 추가하고 애니메이션하는 방법을 설명하겠습니다. Flutter Maps 위젯을 처음 사용하는 경우 진행하기 전에 다음 블로그 게시물을 참조하세요.

  • Introducing Flutter Maps Widget [블로그]

  • [블로그]

  • 로드 시간에 애니메이션 마커 추가



    로드 시 애니메이션 마커를 추가하려면 애니메이션 위젯을 MapMarker의 자식으로 추가하고 initState에서 애니메이션을 시작해야 합니다.

    다음 예에서는 로드 시간에 네 번째 인덱스 마커를 애니메이션했습니다.

    class _LoadAnimatableMarkerAtLoadtime extends StatefulWidget {
      @override
      _LoadAnimatableMarkerAtLoadtimeState createState() =>
          _LoadAnimatableMarkerAtLoadtimeState();
    }
    
    class _LoadAnimatableMarkerAtLoadtimeState
        extends State<_LoadAnimatableMarkerAtLoadtime>
        with SingleTickerProviderStateMixin {
      late AnimationController _controller;
      late CurvedAnimation _animation;
      late Map<String, MapLatLng> _markers;
    
      int _selectedMarkerIndex = 4;
    
      @override
      void initState() {
        _controller = AnimationController(
            vsync: this, duration: const Duration(milliseconds: 750));
        _animation =
            CurvedAnimation(parent: _controller, curve: Curves.easeOutBack);
    
        _markers = <String, MapLatLng>{
          'Chad': MapLatLng(15.454166, 18.732206),
          'Nigeria': MapLatLng(9.081999, 8.675277),
          'DRC': MapLatLng(-4.038333, 21.758663),
          'CAR': MapLatLng(6.600281, 20.480205),
          'Sudan': MapLatLng(12.862807, 30.217636),
          'Kenya': MapLatLng(0.0236, 37.9062),
          'Zambia': MapLatLng(-10.974129, 30.861397),
          'Egypt': MapLatLng(25.174109, 28.776359),
          'Algeria': MapLatLng(24.276672, 7.308186),
        };
        _controller.repeat(min: 0.15, max: 1.0, reverse: true);
        super.initState();
      }
    
      @override
      void dispose() {
        _controller.stop();
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Load animatable marker at loadtime'),
          ),
          body: MapTileLayer(
            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
            initialZoomLevel: 3,
            initialFocalLatLng: MapLatLng(2.3104, 16.5581),
            initialMarkersCount: _markers.length,
            markerBuilder: (BuildContext context, int index) {
              final double size = _selectedMarkerIndex == index ? 40 : 25;
              final MapLatLng markerData = _markers.values.elementAt(index);
              final Icon current = Icon(Icons.location_pin, size: size);
              return MapMarker(
                latitude: markerData.latitude,
                longitude: markerData.longitude,
                child: Transform.translate(
                  offset: Offset(0.0, -size / 2),
                  child: _selectedMarkerIndex == index
                      ? ScaleTransition(
                          alignment: Alignment.bottomCenter,
                          scale: _animation,
                          child: current)
                      : current,
                ),
              );
            },
          ),
        );
      }
    }
    


    Flutter 맵의 초기 로드 중 애니메이션 마커 추가

    프로그래밍 방식으로 마커 추가



    마커를 동적으로 추가하거나 업데이트하려면 마커를 포함하기 위해 MapShapeLayerController 또는 MapTileLayerController을 사용하는지 여부에 따라 MapShapeLayer 또는 MapTileLayer의 도움이 필요합니다. MapShapeLayerController 및 MapTileLayerController는 마커를 추가, 제거, 업데이트 및 지우는 방식으로 마커 컬렉션을 동적으로 제어합니다.

    다음 예제에서는 insertMarkerMapShapeLayerController 메서드를 사용하여 정확한 좌표에 마커를 동적으로 추가합니다. 여기서는 Icon 위젯을 마커로 사용합니다. MapShapeLayerControl의 insertMarker 메서드를 호출하면 해당 인덱스와 함께 TileLayermarkerBuilder 콜백이 트리거됩니다. 여기서 현재 인덱스에서 업데이트할 MapMarker을 반환해야 합니다.

    class AddingMarkerDynamically extends StatefulWidget {
      @override
      _AddingMarkerDynamicallyState createState() =>
          _AddingMarkerDynamicallyState();
    }
    
    class _AddingMarkerDynamicallyState extends State
        with SingleTickerProviderStateMixin {
      late MapTileLayerController _tileLayerController;
    
      @override
      void initState() {
        _tileLayerController = MapTileLayerController();
        super.initState();
      }
    
      @override
      void dispose() {
        _tileLayerController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Add a marker dynamically'),
          ),
          body: MapTileLayer(
            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
            initialZoomLevel: 3,
            initialFocalLatLng: MapLatLng(2.3104, 16.5581),
            controller: _tileLayerController,
            markerBuilder: (BuildContext context, int index) {
              return MapMarker(
                latitude: 6.6111,
                longitude: 20.9394,
                child: Transform.translate(
                  offset: Offset(0.0, -20.0),
                  child: Icon(Icons.location_pin, size: 40),
                ),
              );
            },
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add_location),
            onPressed: () => _tileLayerController.insertMarker(0),
          ),
        );
      }
    }
    


    Flutter 지도에서 애니메이션 마커를 동적으로 추가

    마커 또는 마커 그룹을 동적으로 애니메이션화



    동적 마커 업데이트에는 MapShapeLayerControllerMapTileLayerController 의 도움이 필요합니다. 다음 예제는 MapMarker 의 자식을 애니메이션 위젯으로 동적으로 업데이트하여 애니메이션을 시작하는 방법을 보여줍니다.

    동시에 각각의 마커 인덱스와 함께 호출updateMarkers하여 마커를 업데이트합니다.

    class AnimateGroupOfMarkersDynamically extends StatefulWidget {
      @override
      _AnimateGroupOfMarkersDynamicallyState createState() =>
          _AnimateGroupOfMarkersDynamicallyState();
    }
    
    class _AnimateGroupOfMarkersDynamicallyState
        extends State
        with TickerProviderStateMixin {
      late AnimationController _controller;
      late CurvedAnimation _animation;
      late MapTileLayerController _tileLayerController;
      late Map<String, MapLatLng> _markers;
    
      List _selectedMarkerIndices = [];
    
      @override
      void initState() {
        _controller = AnimationController(
            vsync: this,
            duration: const Duration(milliseconds: 750),
            reverseDuration: const Duration(milliseconds: 750));
        _animation =
            CurvedAnimation(parent: _controller, curve: Curves.easeOutBack);
        _tileLayerController = MapTileLayerController();
    
        _markers = <String, MapLatLng>{
          'Chad': MapLatLng(15.454166, 18.732206),
          'Nigeria': MapLatLng(9.081999, 8.675277),
          'DRC': MapLatLng(-4.038333, 21.758663),
          'CAR': MapLatLng(6.600281, 20.480205),
          'Sudan': MapLatLng(12.862807, 30.217636),
          'Kenya': MapLatLng(0.0236, 37.9062),
          'Zambia': MapLatLng(-10.974129, 30.861397),
          'Egypt': MapLatLng(25.174109, 28.776359),
          'Algeria': MapLatLng(24.276672, 7.308186),
        };
    
        _controller.repeat(min: 0.1, max: 1.0, reverse: true);
        super.initState();
      }
    
      @override
      void dispose() {
        _controller.stop();
        _controller.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(title: Text('Animate group of markers dynamically')),
          body: MapTileLayer(
            urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
            initialZoomLevel: 3,
            initialFocalLatLng: MapLatLng(2.3104, 16.5581),
            controller: _tileLayerController,
            initialMarkersCount: _markers.length,
            markerBuilder: (BuildContext context, int index) {
              final double size = _selectedMarkerIndices.contains(index) ? 40 : 25;
              final MapLatLng markerLatLng = _markers.values.elementAt(index);
              Widget current = Icon(Icons.location_pin, size: size);
              return MapMarker(
                latitude: markerLatLng.latitude,
                longitude: markerLatLng.longitude,
                child: Transform.translate(
                  offset: Offset(0.0, -size / 2),
                  child: _selectedMarkerIndices.contains(index)
                      ? ScaleTransition(
                          alignment: Alignment.bottomCenter,
                          scale: _animation,
                          child: current)
                      : current,
                ),
              );
            },
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.animation),
            onPressed: () {
              _selectedMarkerIndices = [0, 2, 4];
              _tileLayerController.updateMarkers(_selectedMarkerIndices);
              _controller.repeat(min: 0.1, max: 1.0, reverse: true);
            },
          ),
        );
      }
    }
    


    Futter 맵에서 동적으로 마커 애니메이션

    상호 작용 시 마커 애니메이션



    탭 또는 클릭 제스처에서 단일 마커를 애니메이션화할 마커 그룹이 있다고 가정합니다.

    이를 위해 마커의 자식을 GestureDetector 안에 래핑하고 GestureDetector.onTap 콜백을 사용하여 탭 상호 작용을 처리해야 합니다. 탭할 때 마커 인덱스로 updateMarkers을 호출하여 애니메이션을 적용합니다.

    다음 코드는 이를 보여줍니다.

    int _selectedMarkerIndex = -1;
    int _prevSelectedMarkerIndex = -1;
    
    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text('Animate a single marker dynamically'),
        ),
        body: MapTileLayer(
          urlTemplate: 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
          initialZoomLevel: 3,
          initialFocalLatLng: MapLatLng(2.3104, 16.5581),
          controller: _tileLayerController,
          initialMarkersCount: _markers.length,
          markerBuilder: (BuildContext context, int index) =>
              _buildMarker(context, index),
        ),
      );
    }
    
    MapMarker _buildMarker(BuildContext context, int index) {
      final double size = _selectedMarkerIndex == index ? 40 : 25;
      final MapLatLng markerLatLng = _markers.values.elementAt(index);
      final Icon current = Icon(Icons.location_pin);
      return MapMarker(
        latitude: markerLatLng.latitude,
        longitude: markerLatLng.longitude,
        child: GestureDetector(
          onTap: () {
            _prevSelectedMarkerIndex = _selectedMarkerIndex;
            _selectedMarkerIndex = index;
            _tileLayerController.updateMarkers([
              if (_prevSelectedMarkerIndex != -1) _prevSelectedMarkerIndex,
              _selectedMarkerIndex
            ]);
          },
          child: AnimatedContainer(
            transform: Matrix4.identity()..translate(0.0, -size / 2),
            duration: const Duration(milliseconds: 250),
            height: size,
            width: size,
            child: FittedBox(child: current),
          ),
        ),
      );
    }
    


    Flutter 지도의 상호 작용에 대한 마커 애니메이션

    자원



    자세한 내용은 GitHub에서 이animating markers on Flutter Maps project를 참조하세요.

    결론



    이 블로그에서는 로드 시 동적으로 그리고 SyncfusionFlutter Maps 위젯의 상호 작용을 통해 단일 마커 및 마커 그룹에 애니메이션을 적용하는 방법을 배웠습니다. 사용해 보고 아래 댓글 섹션에 피드백을 남겨주세요.

    user guide에서 Flutter Maps의 다른 기능을 확인하고 Flutter Maps project samples을 살펴보십시오. 또한 Android , iOS , web , WindowsLinux 과 같은 다양한 플랫폼에서 사용할 수 있는 데모 앱을 확인하십시오.

    Flutter 프레임워크용 새 위젯이나 기존 위젯의 새 기능이 필요한 경우 support forums , Direct-Trac 또는 feedback portal 를 통해 문의할 수 있습니다. 언제나처럼 기꺼이 도와드리겠습니다!

    이 블로그 게시물이 마음에 들면 다음 기사도 마음에 드실 것입니다.

  • [블로그]

  • [블로그]

  • [블로그]
  • 좋은 웹페이지 즐겨찾기