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는 마커를 추가, 제거, 업데이트 및 지우는 방식으로 마커 컬렉션을 동적으로 제어합니다.

    다음 예제에서는 insertMarker의 MapShapeLayerController 메서드를 사용하여 정확한 좌표에 마커를 동적으로 추가합니다. 여기서는 Icon 위젯을 마커로 사용합니다. MapShapeLayerControl의 insertMarker 메서드를 호출하면 해당 인덱스와 함께 TileLayer 의 markerBuilder 콜백이 트리거됩니다. 여기서 현재 인덱스에서 업데이트할 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 지도에서 애니메이션 마커를 동적으로 추가

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



    동적 마커 업데이트에는 MapShapeLayerController 및 MapTileLayerController 의 도움이 필요합니다. 다음 예제는 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 , Windows 및 Linux 과 같은 다양한 플랫폼에서 사용할 수 있는 데모 앱을 확인하십시오.

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

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

  • [블로그]

  • [블로그]

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