在 flutter 中使用 Google 地图(适用于移动设备和网络)

发布于 2025-01-09 20:40:57 字数 1624 浏览 5 评论 0原文

我正在开发一个 flutter 应用程序,该应用程序应该有一个适用于网络和移动设备的通用代码库。

我的应用程序将有一个谷歌地图,据我所知,没有一个包可以满足所有平台。

google_maps_flutter - seems to work only for mobile (IOS / Android)
google_maps_flutter_web - seems to work only for web

因此,很可能我必须使用这些单独的包创建两个单独的 MapWidget,一个用于网络,一个用于移动设备。

对于移动设备:

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

class MapSample extends StatefulWidget {
  MapSample({Key? key}) : super(key: key);

  @override
  State<MapSample> createState() => MapSampleState();
}

class MapSampleState extends State<MapSample> {
  final Completer<GoogleMapController> _controller = Completer();

  static const CameraPosition _kGooglePlex = CameraPosition(
    target: LatLng(37.42796133580664, -122.085749655962),
    zoom: 14.4746,
  );

  @override
  Widget build(BuildContext context) {
    return GoogleMap(
      mapType: MapType.hybrid,
      initialCameraPosition: _kGooglePlex,
      onMapCreated: (GoogleMapController controller) {
        _controller.complete(controller);
      },
    );
  }
}

对于网络来说,情况有点复杂,据我所知,google_maps_flutter_web实际上并不是一个可用的版本(如果我错了,请纠正我),它实际上使用另一个不是由 flutter 团队 google_maps 6.0.0 开发的软件包。

google_maps_flutter_web 的目标可能是拥有与 google_maps_flutter (google_maps_flutter_platform_interface) 相同的 api 并无缝使用它,但我真的找不到如何使用它的示例...

我应该怎么做?我对 google_maps_flutter_web 有什么误解并且它确实有效吗?或者我应该尝试使用实际上适用于网络的 google_maps 并根据 kIsWeb 切换小部件?

I'm working on an flutter application that should have a common codebase for web and mobile.

My app will have a google map and as far as I've seen there's not a single package to satisfy all platforms.

google_maps_flutter - seems to work only for mobile (IOS / Android)
google_maps_flutter_web - seems to work only for web

So most probably I have to create two separate MapWidgets, one for the web and one for mobile using these separate packages.

For mobile:

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

class MapSample extends StatefulWidget {
  MapSample({Key? key}) : super(key: key);

  @override
  State<MapSample> createState() => MapSampleState();
}

class MapSampleState extends State<MapSample> {
  final Completer<GoogleMapController> _controller = Completer();

  static const CameraPosition _kGooglePlex = CameraPosition(
    target: LatLng(37.42796133580664, -122.085749655962),
    zoom: 14.4746,
  );

  @override
  Widget build(BuildContext context) {
    return GoogleMap(
      mapType: MapType.hybrid,
      initialCameraPosition: _kGooglePlex,
      onMapCreated: (GoogleMapController controller) {
        _controller.complete(controller);
      },
    );
  }
}

For the web, it's a bit more complicated, it seems that google_maps_flutter_web isn't actually an usable version, from what I understand, (correct me if I'm wrong) and it actually uses another package that's not developed by the flutter team google_maps 6.0.0.

The objective of google_maps_flutter_web probably is to have the same api as google_maps_flutter (google_maps_flutter_platform_interface) and use it seamlessly, but I couldn't really find an example of how to use it...

How should I go about this? Any change I'm mistaken about google_maps_flutter_web and it actually works? Or I should just try to use google_maps which actually works for the web and just switch widgets based on kIsWeb?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

安人多梦 2025-01-16 20:40:57

编辑:新的官方插件已发布:https://pub.dev/packages/google_maps_flutter_web .它已经可以与现有的 google_maps_flutter 插件配合使用,只需在 web/index.html 中添加您的 api 脚本即可。

正如用户 exilonX 所建议的,当前(22 年 4 月)在 Flutter 网页版和移动版上使用 Google 地图的方式,就是根据设备动态加载库。然而,他的回答缺乏一些重要的细节。我花了将近 1 个小时才让他的工作正常工作,因此我在这里分享一个更清晰、更有组织的解决方案,希望它能为您节省一些时间(由于编辑队列很长,我无法编辑他的答案)。


文件夹结构:

\widget
   \map_widget.dart
   \web_map_widget.dart
   \mob_map_widget.dart
   \map_widget_stub.dart

MapWidget:

在文件 map_widget.dart 中,您将拥有抽象的 MapWidget:

import 'package:flutter/material.dart';

import 'map_widget_stub.dart'
    if (dart.library.html) 'web_map_widget.dart'
    if (dart.library.io) 'mob_map_widget.dart';

abstract class MapWidget extends StatefulWidget {
  factory MapWidget() => getMapWidget();
}

注意:条件中需要的唯一半列import 位于第二个 if 的末尾。


Web MapWidget:

此文件将包含网络上显示的谷歌地图:

import 'dart:html';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:google_maps/google_maps.dart';

import 'map_widget.dart';

MapWidget getMapWidget() => WebMap();

class WebMap extends StatefulWidget implements MapWidget {
  WebMap({Key? key}) : super(key: key);

  @override
  State<WebMap> createState() => WebMapState();
}

class WebMapState extends State<WebMap> {
  @override
  Widget build(BuildContext context) {
    final String htmlId = "map";

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
      final mapOptions = MapOptions()
        ..zoom = 15.0
        ..center = LatLng(35.7560423, 139.7803552);

      final elem = DivElement()..id = htmlId;
      final map = GMap(elem, mapOptions);

      map.onCenterChanged.listen((event) {});
      map.onDragstart.listen((event) {});
      map.onDragend.listen((event) {});

      Marker(MarkerOptions()
        ..position = map.center
        ..map = map);

      return elem;
    });
    return HtmlElementView(viewType: htmlId);
  }
}

在这里您可以找到有关 Web 实现的更多详细信息。


移动MapWidget:

此文件包含移动设备(android/ios)的实现:

import 'dart:async';

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

import 'map_widget.dart';

MapWidget getMapWidget() => MobileMap();

class MobileMap extends StatefulWidget implements MapWidget {
  MobileMap({Key? key}) : super(key: key);

  @override
  State<MobileMap> createState() => MobileMapState();
}

class MobileMapState extends State<MobileMap> {
  final Completer<GoogleMapController> _controller = Completer();

  static const CameraPosition _kFalentexHouse =
      CameraPosition(target: LatLng(44.497858579692135, 11.336362079086408));

  @override
  Widget build(BuildContext context) {
    return GoogleMap(
      mapType: MapType.hybrid,
      initialCameraPosition: _kFalentexHouse,
      onMapCreated: (GoogleMapController controller) {
        _controller.complete(controller);
      },
    );
  }
}

存根

最后,您需要一个存根:

import 'map_widget.dart';

//the error is shown in case of wrong version loaded on wrong platform
MapWidget getMapWidget() => throw UnsupportedError(
    'Cannot create a map without dart:html or google_maps_flutter');

用法

现在您可以使用将小部件 MapWidget 作为普通小部件:

Scaffold(
      body: Center(
        child: SizedBox(
          height: 300,
          width: 300,
          child: MapWidget(),
        ),
      ),
    );

注意:为了使地图正常工作,您需要使用 键对其进行设置。请参阅移动版网络

EDIT: A new official plugin has been released: https://pub.dev/packages/google_maps_flutter_web . It already works with the existing google_maps_flutter plugin, just add your api script in the web/index.html .

As the user exilonX suggested, the current way (Apr '22) to use Google Maps on both Flutter web and mobile, is to load the library dynamically based on the device. However, his answer lacks of some important details. It took me almost 1h to make his work working, therefore I'm sharing here a clearer and more organized solution, hopefully it'll save you some time (I couldn't edit his answer due to long edit queue).


Folder structure:

\widget
   \map_widget.dart
   \web_map_widget.dart
   \mob_map_widget.dart
   \map_widget_stub.dart

MapWidget:

In the file map_widget.dart you'll have the abstract MapWidget:

import 'package:flutter/material.dart';

import 'map_widget_stub.dart'
    if (dart.library.html) 'web_map_widget.dart'
    if (dart.library.io) 'mob_map_widget.dart';

abstract class MapWidget extends StatefulWidget {
  factory MapWidget() => getMapWidget();
}

NOTE: the only semicolumn you need on the conditional import is at the end of the second if.


Web MapWidget:

This file will contain the google map shown on web:

import 'dart:html';
import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:google_maps/google_maps.dart';

import 'map_widget.dart';

MapWidget getMapWidget() => WebMap();

class WebMap extends StatefulWidget implements MapWidget {
  WebMap({Key? key}) : super(key: key);

  @override
  State<WebMap> createState() => WebMapState();
}

class WebMapState extends State<WebMap> {
  @override
  Widget build(BuildContext context) {
    final String htmlId = "map";

    // ignore: undefined_prefixed_name
    ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
      final mapOptions = MapOptions()
        ..zoom = 15.0
        ..center = LatLng(35.7560423, 139.7803552);

      final elem = DivElement()..id = htmlId;
      final map = GMap(elem, mapOptions);

      map.onCenterChanged.listen((event) {});
      map.onDragstart.listen((event) {});
      map.onDragend.listen((event) {});

      Marker(MarkerOptions()
        ..position = map.center
        ..map = map);

      return elem;
    });
    return HtmlElementView(viewType: htmlId);
  }
}

Here you can find more details about the web implementation.


Mobile MapWidget:

This file contains the implementation for mobile (android/ios):

import 'dart:async';

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

import 'map_widget.dart';

MapWidget getMapWidget() => MobileMap();

class MobileMap extends StatefulWidget implements MapWidget {
  MobileMap({Key? key}) : super(key: key);

  @override
  State<MobileMap> createState() => MobileMapState();
}

class MobileMapState extends State<MobileMap> {
  final Completer<GoogleMapController> _controller = Completer();

  static const CameraPosition _kFalentexHouse =
      CameraPosition(target: LatLng(44.497858579692135, 11.336362079086408));

  @override
  Widget build(BuildContext context) {
    return GoogleMap(
      mapType: MapType.hybrid,
      initialCameraPosition: _kFalentexHouse,
      onMapCreated: (GoogleMapController controller) {
        _controller.complete(controller);
      },
    );
  }
}

Stub

Finally, you need a stub:

import 'map_widget.dart';

//the error is shown in case of wrong version loaded on wrong platform
MapWidget getMapWidget() => throw UnsupportedError(
    'Cannot create a map without dart:html or google_maps_flutter');

Usage

Now you can use the widget MapWidget as a normal widget:

Scaffold(
      body: Center(
        child: SizedBox(
          height: 300,
          width: 300,
          child: MapWidget(),
        ),
      ),
    );

NOTE: in order to make the map work you need to set it up with the key. See the official library documentation for mobile and web.

江南月 2025-01-16 20:40:57

最终我找到了一种使用 google_maps这个答案作为灵感的解决方法:

  1. Abstract MapWidget
    import 'package:client_ojp4danube/map/map_widget_stub.dart'
        if (dart.library.html) 'package:client_ojp4danube/map/map_web_widget.dart'
        if (dart.library.io) 'package:client_ojp4danube/map/map_widget.dart';
    
    import 'package:flutter/material.dart';
    
    abstract class MapWidget extends StatefulWidget {
      factory MapWidget() => getMapWidget();
    }
  1. 使用 WebMap 小部件google_maps:
    import 'dart:html';
    
    import 'package:client_ojp4danube/map/abstract_map_widget.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:google_maps/google_maps.dart';
    import 'dart:ui' as ui;
    
    Widget getMap() {
      String htmlId = "7";
    
      // ignore: undefined_prefixed_name
      ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
        final myLatlng = new LatLng(30.2669444, -97.7427778);
    
        final mapOptions = new MapOptions()
          ..zoom = 8
          ..center = new LatLng(30.2669444, -97.7427778);
    
        final elem = DivElement()
          ..id = htmlId
          ..style.width = "100%"
          ..style.height = "100%"
          ..style.border = 'none';
    
        final map = GMap(elem, mapOptions);
    
        Marker(MarkerOptions()
          ..position = myLatlng
          ..map = map
          ..title = 'Hello World!');
    
        return elem;
      });
    
      return HtmlElementView(viewType: htmlId);
    }
    
    class WebMap extends StatefulWidget implements MapWidget {
      WebMap({Key? key}) : super(key: key);
    
      @override
      State<WebMap> createState() => WebMapState();
    }
    
    class WebMapState extends State<WebMap> {
      @override
      Widget build(BuildContext context) {
        return getMap();
      }
    }
    
    MapWidget getMapWidget() {
      print("Intra in get map web ");
      return WebMap();
    }
  1. 移动地图小部件
    import 'dart:async';
    
    import 'package:client_ojp4danube/map/abstract_map_widget.dart';
    import 'package:flutter/material.dart';
    import 'package:google_maps_flutter/google_maps_flutter.dart';
    
    class MobileMap extends StatefulWidget implements MapWidget {
      MobileMap({Key? key}) : super(key: key);
    
      @override
      State<MobileMap> createState() => MobileMapState();
    }
    
    class MobileMapState extends State<MobileMap> {
      final Completer<GoogleMapController> _controller = Completer();
    
      static const CameraPosition _kGooglePlex = CameraPosition(
        target: LatLng(37.42796133580664, -122.085749655962),
        zoom: 14.4746,
      );
    
      @override
      Widget build(BuildContext context) {
        return GoogleMap(
          mapType: MapType.hybrid,
          initialCameraPosition: _kGooglePlex,
          onMapCreated: (GoogleMapController controller) {
            _controller.complete(controller);
          },
        );
      }
    }
    
    MapWidget getMapWidget() {
      return MobileMap();
    }
  1. getMapWidget - 存根
    import 'package:client_ojp4danube/map/abstract_map_widget.dart';
    
    // Created because importing dart.html on a mobile app breaks the build
    MapWidget getMapWidget() => throw UnsupportedError(
        'Cannot create a map without dart:html or google_maps_flutter');
  1. 实际上使用抽象小部件,它将返回适合平台的小部件
import 'package:client_ojp4danube/map/abstract_map_widget.dart';


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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(child: MapWidget()),
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

Eventually I found a workaround using google_maps and this answer as inspiration:

  1. Abstract MapWidget
    import 'package:client_ojp4danube/map/map_widget_stub.dart'
        if (dart.library.html) 'package:client_ojp4danube/map/map_web_widget.dart'
        if (dart.library.io) 'package:client_ojp4danube/map/map_widget.dart';
    
    import 'package:flutter/material.dart';
    
    abstract class MapWidget extends StatefulWidget {
      factory MapWidget() => getMapWidget();
    }
  1. WebMap widget that uses google_maps:
    import 'dart:html';
    
    import 'package:client_ojp4danube/map/abstract_map_widget.dart';
    import 'package:flutter/cupertino.dart';
    import 'package:google_maps/google_maps.dart';
    import 'dart:ui' as ui;
    
    Widget getMap() {
      String htmlId = "7";
    
      // ignore: undefined_prefixed_name
      ui.platformViewRegistry.registerViewFactory(htmlId, (int viewId) {
        final myLatlng = new LatLng(30.2669444, -97.7427778);
    
        final mapOptions = new MapOptions()
          ..zoom = 8
          ..center = new LatLng(30.2669444, -97.7427778);
    
        final elem = DivElement()
          ..id = htmlId
          ..style.width = "100%"
          ..style.height = "100%"
          ..style.border = 'none';
    
        final map = GMap(elem, mapOptions);
    
        Marker(MarkerOptions()
          ..position = myLatlng
          ..map = map
          ..title = 'Hello World!');
    
        return elem;
      });
    
      return HtmlElementView(viewType: htmlId);
    }
    
    class WebMap extends StatefulWidget implements MapWidget {
      WebMap({Key? key}) : super(key: key);
    
      @override
      State<WebMap> createState() => WebMapState();
    }
    
    class WebMapState extends State<WebMap> {
      @override
      Widget build(BuildContext context) {
        return getMap();
      }
    }
    
    MapWidget getMapWidget() {
      print("Intra in get map web ");
      return WebMap();
    }
  1. Mobile Map Widget
    import 'dart:async';
    
    import 'package:client_ojp4danube/map/abstract_map_widget.dart';
    import 'package:flutter/material.dart';
    import 'package:google_maps_flutter/google_maps_flutter.dart';
    
    class MobileMap extends StatefulWidget implements MapWidget {
      MobileMap({Key? key}) : super(key: key);
    
      @override
      State<MobileMap> createState() => MobileMapState();
    }
    
    class MobileMapState extends State<MobileMap> {
      final Completer<GoogleMapController> _controller = Completer();
    
      static const CameraPosition _kGooglePlex = CameraPosition(
        target: LatLng(37.42796133580664, -122.085749655962),
        zoom: 14.4746,
      );
    
      @override
      Widget build(BuildContext context) {
        return GoogleMap(
          mapType: MapType.hybrid,
          initialCameraPosition: _kGooglePlex,
          onMapCreated: (GoogleMapController controller) {
            _controller.complete(controller);
          },
        );
      }
    }
    
    MapWidget getMapWidget() {
      return MobileMap();
    }
  1. getMapWidget - stub
    import 'package:client_ojp4danube/map/abstract_map_widget.dart';
    
    // Created because importing dart.html on a mobile app breaks the build
    MapWidget getMapWidget() => throw UnsupportedError(
        'Cannot create a map without dart:html or google_maps_flutter');
  1. Actually using the abstract widget that will return the widget suited for the platform
import 'package:client_ojp4danube/map/abstract_map_widget.dart';


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

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _counter = 0;

  void _incrementCounter() {
    setState(() {
      _counter++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Expanded(child: MapWidget()),
            const Text(
              'You have pushed the button this many times:',
            ),
            Text(
              '$_counter',
              style: Theme.of(context).textTheme.headline4,
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _incrementCounter,
        tooltip: 'Increment',
        child: const Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文