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

exi*_*onX 7 google-maps flutter flutter-web google-maps-flutter

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

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

google_maps_flutter - seems to work only for mobile (IOS / Android)
google_maps_flutter_web - seems to work only for web
Run Code Online (Sandbox Code Playgroud)

因此,很可能我必须使用这些单独的包创建两个单独的 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);
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

对于网络来说,它有点复杂,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

Leo*_*ese 6

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

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


文件夹结构:

\widget
   \map_widget.dart
   \web_map_widget.dart
   \mob_map_widget.dart
   \map_widget_stub.dart
Run Code Online (Sandbox Code Playgroud)

地图小部件:

在该文件中,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();
}
Run Code Online (Sandbox Code Playgroud)

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


网络地图小部件:

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

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

在这里您可以找到有关网络实施的更多详细信息。


移动地图小工具:

该文件包含移动设备(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);
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

存根

最后,您需要一个存根:

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');
Run Code Online (Sandbox Code Playgroud)

用法

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

Scaffold(
      body: Center(
        child: SizedBox(
          height: 300,
          width: 300,
          child: MapWidget(),
        ),
      ),
    );
Run Code Online (Sandbox Code Playgroud)

注意:为了使地图正常工作,您需要使用密钥进行设置。请参阅移动网络的官方库文档。


exi*_*onX 5

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

  1. 抽象地图小部件
    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();
    }
Run Code Online (Sandbox Code Playgroud)
  1. 使用 google_maps 的 WebMap 小部件:
    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();
    }
Run Code Online (Sandbox Code Playgroud)
  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();
    }
Run Code Online (Sandbox Code Playgroud)
  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');
Run Code Online (Sandbox Code Playgroud)
  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.
    );
  }
}

Run Code Online (Sandbox Code Playgroud)