Flutter图像叠加地图插件image_overlay_map的使用

Flutter图像叠加地图插件image_overlay_map的使用

这是一个用于在Flutter中使用本地或网络图片构建地图的插件。它能够自定义标记并根据屏幕缩放重新构建标记。

指导

this.child

地图使用的图片。它会根据设备的不同自动调整大小以适应全屏。

支持本地资源或网络URL。示例演示了如何使用网络图片。

// 使用网络图片作为地图背景
_getWebMap("https://user-images.githubusercontent.com/11880676/117417895-c6f5a380-af55-11eb-8fe4-5db2f40ca82a.png")

this.size

图片的像素尺寸。

示例使用Leaflet CRS.Simple,范围为 [[height / 2, width / 2], [-height / 2, -width / 2]][0,0] 是图片中心。你可以根据你的标记数据构建一个不同的坐标系。

// 计算图片尺寸
Future<Size> _calculateImageDimension(String imageUrl) {
  Completer<Size> completer = Completer();
  Image image = new Image(image: CachedNetworkImageProvider(imageUrl));
  image.image.resolve(ImageConfiguration()).addListener(
    ImageStreamListener(
      (ImageInfo image, bool synchronousCall) {
        var myImage = image.image;
        Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
        completer.complete(size);
      },
    ),
  );
  return completer.future;
}

this.markers

从标记数据构建的MarkerModel数据。

// 获取标记数据
List<MarkerModel> _getMarker(List<Facility> facilities, Size size) {
  List<MarkerModel> result = [];
  facilities.forEach((element) {
    double dx = size.width / 2 + element.lng;
    double dy = size.height / 2 - element.lat;
    result.add(MarkerModel(element, Offset(dx, dy)));
  });
  return result;
}

this.onMarkerClicked

当标记小部件被点击时调用。

// 当标记被点击时弹出对话框
_onMarkerClicked(MarkerModel markerModel) {
  return showDialog(
      context: context,
      builder: (BuildContext context) {
        return Center(
          child: Text((markerModel.data as Facility).name),
        );
      },
      routeSettings: RouteSettings(name: "/facilityDetail"));
}

this.markerWidgetBuilder

从MarkerModel构建一个小部件。可以根据不同的缩放值构建不同的小部件。

// 构建标记小部件
Widget _getMarkerWidget(double scale, MarkerModel data) {
  Facility facility = data.data;
  if (facility.facilityId == 1) {
    return Icon(Icons.location_on, color: Colors.blue);
  }

  if (scale > 3) {
    return BubbleWidget(
        direction: ArrowDirection.bottom,
        color: Colors.orange,
        strokeColor: Colors.white,
        strokeWidth: 1.0,
        borderRadius: 3.0,
        style: BubbleStyle.stroke,
        padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
        child: Text(facility.name,
            maxLines: 1,
            textAlign: TextAlign.left,
            style: TextStyle(color: Colors.white, fontSize: 10.0)));
  }

  return Icon(Icons.location_on, color: Colors.redAccent);
}

this.onTab

当地图背景图片被点击时调用。

// 当地图背景图片被点击时打印日志
_onTab() {
  print("onTab");
}

完整示例代码

以下是完整的示例代码,展示了如何使用 image_overlay_map 插件来创建一个具有可点击标记的地图:

import 'dart:async';

import 'package:cached_network_image/cached_network_image.dart';
import 'package:flutter/material.dart';
import 'package:image_overlay_map/image_overlay_map.dart';
import 'bubble_widget.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // 这个小部件是你的应用的根。
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'image overlay map Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'map like leaflet image overlay'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, required this.title}) : super(key: key);

  final String title;
  final List<Facility> _facilityList = [
    Facility(1, "facility1", 0, 0), // 中心
    Facility(2, "facility2", -100, 0),
    Facility(3, "facility3", 100, 0),
    Facility(4, "facility4", 0, -100),
    Facility(5, "facility5", 0, 100),
  ];

  [@override](/user/override)
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text(widget.title),
        ),
        body: Stack(
          children: <Widget>[
            _getWebMap(
                "https://user-images.githubusercontent.com/11880676/117417895-c6f5a380-af55-11eb-8fe4-5db2f40ca82a.png"),
          ],
        ));
  }

  Widget _getWebMap(String url) {
    return FutureBuilder(
        future: _calculateImageDimension(url),
        builder: (BuildContext context, AsyncSnapshot snapShot) {
          if (snapShot.connectionState == ConnectionState.done) {
            if (snapShot.hasError) {
              return Center(
                child: Text("error happened when download image"),
              );
            }
            return Center(
              child: MapContainer(
                  new Image(image: CachedNetworkImageProvider(url)),
                  snapShot.data,
                  markers: _getMarker(widget._facilityList, snapShot.data),
                  markerWidgetBuilder: _getMarkerWidget,
                  onTab: _onTab,
                  onMarkerClicked: _onMarkerClicked),
            );
          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        });
  }

  Future<Size> _calculateImageDimension(String imageUrl) {
    Completer<Size> completer = Completer();
    Image image = new Image(image: CachedNetworkImageProvider(imageUrl));
    image.image.resolve(ImageConfiguration()).addListener(
      ImageStreamListener(
        (ImageInfo image, bool synchronousCall) {
          var myImage = image.image;
          Size size = Size(myImage.width.toDouble(), myImage.height.toDouble());
          completer.complete(size);
        },
      ),
    );
    return completer.future;
  }

  List<MarkerModel> _getMarker(List<Facility> facilities, Size size) {
    List<MarkerModel> result = [];
    facilities.forEach((element) {
      double dx = size.width / 2 + element.lng;
      double dy = size.height / 2 - element.lat;
      result.add(MarkerModel(element, Offset(dx, dy)));
    });
    return result;
  }

  Widget _getMarkerWidget(double scale, MarkerModel data) {
    Facility facility = data.data;
    if (facility.facilityId == 1) {
      return Icon(Icons.location_on, color: Colors.blue);
    }

    if (scale > 3) {
      return BubbleWidget(
          direction: ArrowDirection.bottom,
          color: Colors.orange,
          strokeColor: Colors.white,
          strokeWidth: 1.0,
          borderRadius: 3.0,
          style: BubbleStyle.stroke,
          padding: EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 10.0),
          child: Text(facility.name,
              maxLines: 1,
              textAlign: TextAlign.left,
              style: TextStyle(color: Colors.white, fontSize: 10.0)));
    }

    return Icon(Icons.location_on, color: Colors.redAccent);
  }

  _onMarkerClicked(MarkerModel markerModel) {
    return showDialog(
        context: context,
        builder: (BuildContext context) {
          return Center(
            child: Text((markerModel.data as Facility).name),
          );
        },
        routeSettings: RouteSettings(name: "/facilityDetail"));
  }

  _onTab() {
    print("onTab");
  }
}

class Facility {
  int facilityId;
  String name;

  double lng;
  double lat;

  Facility(this.facilityId, this.name, this.lng, this.lat);
}

更多关于Flutter图像叠加地图插件image_overlay_map的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图像叠加地图插件image_overlay_map的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter中使用image_overlay_map插件来实现图像叠加在地图上的代码示例。这个插件允许你将一张图片叠加在地图(如Google Maps或Apple Maps)之上,常用于显示特定区域的自定义覆盖图。

首先,确保你已经在pubspec.yaml文件中添加了image_overlay_map依赖:

dependencies:
  flutter:
    sdk: flutter
  image_overlay_map: ^最新版本号  # 替换为实际的最新版本号

然后,运行flutter pub get来安装依赖。

接下来是一个简单的示例代码,展示如何使用image_overlay_map插件:

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:image_overlay_map/image_overlay_map.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Image Overlay Map Example'),
        ),
        body: MapWithOverlay(),
      ),
    );
  }
}

class MapWithOverlay extends StatefulWidget {
  @override
  _MapWithOverlayState createState() => _MapWithOverlayState();
}

class _MapWithOverlayState extends State<MapWithOverlay> {
  late GoogleMapController _controller;

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: <Widget>[
        GoogleMap(
          mapType: MapType.hybrid,
          initialCameraPosition: CameraPosition(
            target: LatLng(37.7749, -122.4194),
            zoom: 12.0,
          ),
          onMapCreated: (GoogleMapController controller) {
            _controller = controller;
          },
        ),
        Positioned.fill(
          child: ImageOverlayMap(
            imageProvider: NetworkImage(
              'https://example.com/your-overlay-image.png',  // 替换为你的图片URL
            ),
            mapController: _controller,
            bounds: LatLngBounds(
              northeast: LatLng(37.78, -122.41),
              southwest: LatLng(37.77, -122.43),
            ),
          ),
        ),
      ],
    );
  }
}

解释:

  1. 依赖添加

    • pubspec.yaml中添加image_overlay_mapgoogle_maps_flutter(用于显示Google Maps)。
  2. 基础布局

    • 使用Stack小部件来叠加地图和图像。
  3. Google Maps

    • 使用GoogleMap小部件显示地图,并设置初始相机位置和地图类型。
    • 使用onMapCreated回调获取GoogleMapController实例,以便后续操作。
  4. 图像叠加

    • 使用ImageOverlayMap小部件将图像叠加在地图上。
    • imageProvider属性指定图像源(可以是网络图片或本地图片)。
    • mapController属性绑定到之前获取的GoogleMapController实例。
    • bounds属性定义图像覆盖的地理边界(通过LatLngBounds指定)。

注意事项:

  • 确保你已经按照Google Maps Flutter插件的要求,获取并配置了API密钥。
  • 图像URL需要是一个可访问的网络图片,或者你可以使用AssetImage加载本地图片。
  • 根据实际需要调整LatLngBounds的值,以确保图像正确覆盖在目标区域。

这个示例展示了基本的图像叠加地图功能,你可以根据需要进一步自定义和扩展。

回到顶部