简介

Flutter 是一个移动应用程序开发工具包,用于在创纪录的时间内在 iOS 和 Android 平台上打造高质量的原生体验。尽管 Flutter 足以构建出色的移动应用程序,但仍需要交互性(例如地图集成)来增加用户体验。

华为地图包

Huawei Map Kit是华为开发的开发套件和地图服务,可轻松将基于地图的功能集成到您的应用程序中。该套件目前涵盖200多个国家和地区的地图数据,支持40+语言,提供标记、形状图层等UI元素来自定义您的地图,并且还可以让用户在不同场景下通过手势和按钮与你的应用中的地图进行交互。

借助最近发布的Huawei Map Kit Flutter Plugin,华为开发者现在可以使用这些功能并将基于地图的功能集成到他们的 Flutter 项目中。因此,在本文中,为了探索该套件和华为服务,我们将尝试使用该插件和 Flutter SDK 构建一个具有华为地图的移动应用程序。

HMS 核心 Github:

https://github.com/HMS-Core/hms-flutter-plugin/tree/master/flutter-hms-map

所需配置

在我们开始之前,要使用华为 Map Kit 以及其他华为移动服务,您应该是华为开发者账号持有者。更多关于开发者账号的详细信息以及如何申请,请参考这个链接。

创建应用

· 使用您的华为帐号登录AppGallery Connect并通过单击我的项目>添加项目按钮创建一个新项目来使用。

· 点击添加应用程序按钮,通过填写名称、类别和默认语言等必填字段,将新应用程序添加到您的项目中。

· 默认情况下,您的应用程序的 Map Kit API 是启用的,但为了确保您可以在 AppGallery Connect 上的项目的管理 API 选项卡中检查它。如果您需要任何帮助,也可以参考启用服务文章。

· 打开你的 Android Studio 并创建一个 Flutter 应用程序。您的 Flutter 应用程序的 package name 应与您在 AppGallery Connect 上创建的应用程序的包名相同。

· Android 需要签名证书来验证应用程序的真实性。因此,您需要为您的应用程序生成一个签名证书。如果您不知道如何生成签名证书,请单击此处查看相关文章。将生成的 Keystore 文件复制到项目的 android/app 目录中。

附注:Flutter 项目结构有文件夹,如 iosandroid 文件夹,本质上属于不同的平台,但 Android Studio 将它们视为 Flutter 项目文件夹并在这些文件上抛出错误.因此,在更改与 Android 平台相关的任何内容之前,您应该右键单击项目目录中的 android 文件夹,然后选择 Flutter > Open Android module in Android Studio。您可以轻松地修改文件并从选择后打开的 Android Studio 窗口中生成签名证书。

· 生成签名证书(密钥库)后,您应该使用 JDK 提供的 keytool 提取 SHA-256 指纹,并通过导航到应用程序的 App Information 部分添加到 AppGallery Connect。您也可以参考Generating Fingerprint from a Keystore 和Add Fingerprint certificate to AppGallery Connect文章以获得更多帮助。

将 HMS 和 Map 插件集成到您的 Flutter 项目中

您还需要配置您的 Flutter 应用程序,以便与华为通信以使用 Map Kit。

· 将Huawei Map Kit Flutter Plugin作为依赖添加到您项目的pubspec.yaml文件中,然后运行flutter pub get命令将Map Plugin集成到您的项目中。

dependencies:
  flutter:
    sdk: flutter
  huawei_map: ^4.0.4+300

进入全屏模式 退出全屏模式

· 从 AppGallery Connect 的 App Information 部分下载 agconnect-services.json 文件,并将其复制到项目的 android/app 目录中。

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s---DYj2wPR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev -to -uploads.s3.amazonaws.com/i/rrvzxcwmq73e836kbnnn.png)

添加 agconnect-services.json 和 keystore 文件后,您的项目的 android 目录应如下所示。

· 将Maven仓库地址和AppGallery Connect插件添加到项目级build.gradle(android/build.gradle)文件中。

buildscript {
    repositories {
        //other repositories       
        maven { url 'https://developer.huawei.com/repo/' }
    }
    dependencies {
        //other dependencies       
        classpath 'com.huawei.agconnect:agcp:1.2.1.301'   
    }
}
allprojects {
    repositories {
        //other repositories       
        maven { url 'https://developer.huawei.com/repo/' }
    }
}

进入全屏模式 退出全屏模式

· 打开您的应用程序级 build.gradle (android/app/build.gradle) 文件并添加 AppGallery Connect 插件。

  apply plugin: 'com.android.application'
apply plugin: 'com.huawei.agconnect'  //Added Line
apply plugin: 'kotlin-android'

进入全屏模式 退出全屏模式

· 在同一个文件(android/app/build.gradle)中,添加签名配置,并更改您项目的minSdkVersion,如下所示。

 android {   
   /*    
    * Other configurations    
    */
  defaultConfig {
        applicationId "<package_name>" //Your unique package name
        minSdkVersion 19  //Change minSdkVersion to 19
        targetSdkVersion 28
        versionCode flutterVersionCode.toInteger()
        versionName flutterVersionName
    }
   signingConfigs {      
       config{           
           storeFile file('<keystore_file>')           
           storePassword '<keystore_password>'           
           keyAlias '<key_alias>'           
           keyPassword '<key_password>'       
       }   
   }  

   buildTypes {       
       debug {           
           signingConfig signingConfigs.config
       }      
       release {           
           signingConfig signingConfigs.config
       }   
    }
}

进入全屏模式 退出全屏模式

· 最后,调用Huawei Map Kit的能力,在你的AndroidManifest.xml文件中为你的应用申请以下权限。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

进入全屏模式 退出全屏模式

— 要获取当前设备位置,还需要在您的AndroidManifest.xml 文件中声明以下权限(Android 6.0 及更高版本需要动态申请这些权限)。但是我们不会在我们的应用程序中使用当前位置,这一步是可选的。

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

进入全屏模式 退出全屏模式

使用华为Map Kit Flutter插件

创建地图

现在我们已经准备好使用华为的地图功能了,让我们来实现一个简单的地图。

华为 Map Kit Flutter Plugin 提供了一个名为 HuaweiMap 的小部件,供开发者轻松创建和管理地图片段。通过使用这个小部件,开发人员可以启用或禁用地图的属性或手势,设置初始标记、圆圈或其他形状,确定地图类型,还可以设置初始相机位置以在地图准备好时聚焦某个区域。

让我们从伊斯坦布尔选择一个随机位置作为初始相机位置。在声明初始相机位置时,需要表示放大倍数的缩放级别和表示该位置的纬度和经度的目标。您可以在下面找到我的目标坐标和缩放级别,我们将在创建地图时使用它们。

static const LatLng _center = const LatLng(41.027470, 28.999339);
static const double _zoom = 12;

进入全屏模式 退出全屏模式

由于现在我们有了一个初始位置,我们应该实现地图本身。我们将首先创建一个简单的 Scaffold 并设置一个简单的 AppBar,然后创建一个 HuaweiMap 对象作为 Scaffold 的主体。如前所述,HuaweiMap 对象具有不同的属性,您可以在下面看到。以下代码将创建一个全屏、可滚动、可倾斜的HuaweiMap 对象,并显示建筑物或交通。

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {

  static const LatLng _center = const LatLng(41.027470, 28.999339);
  static const double _zoom = 12;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Map Demo"),
        centerTitle: true,
        backgroundColor: Colors.red,
      ),
      body: HuaweiMap(
        initialCameraPosition: CameraPosition(
          target: _center,
          zoom: _zoom,
        ),
        mapType: MapType.normal,
        tiltGesturesEnabled: true,
        buildingsEnabled: true,
        compassEnabled: true,
        zoomControlsEnabled: false,
        rotateGesturesEnabled: true,
        myLocationButtonEnabled: false,
        myLocationEnabled: false,
        trafficEnabled: true,
      ),
    );
  }
}

进入全屏模式 退出全屏模式

考虑创建一个“干净”的地图,我禁用了地图的 myLocationEnabled、myLocationButtonEnabled 和 zoomControlsEnabled 属性,但不要忘记自己尝试探索这些属性,因为它们非常有助于提升您的应用程序的用户体验。

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--HHF0TOZy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/i/be9rb3iuldsaykf7xa09.jpeg)

调整地图大小

全屏地图在某些场景下并不总是有用,因此,由于 HuaweiMap 是一个独立的小部件,我们可以通过将其包装到 Container 或 ConstrainedBox 小部件来调整地图的大小。

对于这个项目,我将使用 Expanded、Column 和 Container 小部件在 Scaffold 中创建一个布局。以下代码显示了一个仅占屏幕三分之一的 HuaweiMap 小部件。

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {

  static const LatLng _center = const LatLng(41.027470, 28.999339);
  static const double _zoom = 12;

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Locations"),
        centerTitle: true,
        backgroundColor: Colors.red,
      ),
      body: Column(
        children: [
          Expanded(
            child: Padding(
              padding: EdgeInsets.all(8),
              child: Container(
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.green, width: 2)),
                child: HuaweiMap(
                  initialCameraPosition: CameraPosition(
                    target: _center,
                    zoom: _zoom,
                  ),
                  mapType: MapType.normal,
                  tiltGesturesEnabled: true,
                  buildingsEnabled: true,
                  compassEnabled: true,
                  zoomControlsEnabled: false,
                  rotateGesturesEnabled: true,
                  myLocationButtonEnabled: false,
                  myLocationEnabled: false,
                  trafficEnabled: true,
                ),
              ),
            ),
          ),
          Expanded(flex: 2, child: Container()),
        ],
      ),
    );
  }
}

进入全屏模式 退出全屏模式

使用标记和 CameraUpdate 添加交互性

假设我们正在构建一个应用程序,该应用程序将不同的餐厅位置显示为我们 HuaweiMap 对象上的标记。为此,我们将使用 HuaweiMap 小部件的标记字段为我们的地图设置一些初始标记。

小部件的 Markers 字段采用一组标记,因此我们应该首先创建一组 Marker 对象。然后将它们用作我们 HuaweiMap 小部件的初始标记。

如您所知,我们屏幕的三分之二是空的,为了填充空间,我们将创建一些卡片小部件,将位置的名称、座右铭和地址作为字符串保存。为了减少冗余代码块,我创建了一个名为 LocationCard 的单独小部件,它返回一个样式化的自定义 Card 小部件。为了不失去本文的范围,我不会分享如何创建自定义卡片小部件的步骤,但您可以从项目的GitHub链接中找到它的代码。

class HomeScreen extends StatefulWidget {
  @override
  _HomeScreenState createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {

  static const LatLng _center = const LatLng(41.027470, 28.999339);
  static const double _zoom = 12;

  //Marker locations
  static const LatLng _location1 = const LatLng(41.0329109, 28.9840904);
  static const LatLng _location2 = const LatLng(41.0155957, 28.9827176);
  static const LatLng _location3 = const LatLng(41.0217315, 29.0111898);

  //Set of markers
  Set<Marker> _markers = {
    Marker(markerId: MarkerId("Location1"), position: _location1),
    Marker(markerId: MarkerId("Location2"), position: _location2),
    Marker(markerId: MarkerId("Location3"), position: _location3),
  };

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: AppBar(
        title: Text("Locations"),
        centerTitle: true,
        backgroundColor: Colors.red,
      ),
      body: Column(
        children: [
          Expanded(
            child: Padding(
              padding: EdgeInsets.all(8),
              child: Container(
                decoration: BoxDecoration(
                    border: Border.all(color: Colors.green, width: 2)),
                child: HuaweiMap(
                  initialCameraPosition: CameraPosition(
                    target: _center,
                    zoom: _zoom,
                  ),
                  mapType: MapType.normal,
                  tiltGesturesEnabled: true,
                  buildingsEnabled: true,
                  compassEnabled: true,
                  zoomControlsEnabled: false,
                  rotateGesturesEnabled: true,
                  myLocationButtonEnabled: false,
                  myLocationEnabled: false,
                  trafficEnabled: true,
                  markers: _markers, //Using the set
                ),
              ),
            ),
          ),
          //Styled Card widgets
          Expanded(flex: 2, child: Padding(
                  padding: EdgeInsets.all(8),
                  child: SingleChildScrollView(
                    child: Column(
                      children: [
                        LocationCard(
                          title: "Location 1",
                          motto: "A Fine Dining Restaurant",
                          address:
                              "Avrupa Yakası, Cihangir, 34433 Beyoğlu/İstanbul Türkiye",
                        ),
                        LocationCard(
                          title: "Location 2",
                          motto: "A Restaurant with an Extraordinary View",
                          address:
                              "Avrupa Yakası, Hoca Paşa, 34110 Fatih/İstanbul Türkiye",
                        ),
                        LocationCard(
                              title: "Location 3",
                              motto: "A Casual Dining Restaurant",
                              address:
                                  "Anadolu Yakası, Aziz Mahmut Hüdayi, 34672 Üsküdar/İstanbul Türkiye",
                            )
                      ],
                    ),
                  ),),
          )],
      ),
    );
  }
}

进入全屏模式 退出全屏模式

现在我们在地图对象下方有一些自定义卡片,其中也有一些初始标记。我们将使用这些自定义卡片作为按钮,以平滑的相机动画放大所需的标记。为此,用户可以轻松地在一张卡片上查看华为地图上的放大位置,并在不离开页面的情况下探索周围环境。

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--BK_DzDMv--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/i/4twvypztg3o5zv1gztp4.jpg)

使用位置卡调整大小的地图

在将卡片变成按钮之前,我们应该先设置一个HuaweiMapController对象,以便为HuaweiMap提供一个控制器,然后在HuaweiMap小部件onMapCreated字段上使用这个控制器来配对地图及其控制器。下面,我创建了一个控制器,并借助一个简单的函数,在我们的 HuaweiMap 对象中使用它。

 HuaweiMapController mapController;

  void _onMapCreated(HuaweiMapController controller) {
    mapController = controller;
  }

/*
  This section only shows the added line. Remaining code is not changed.
*/
                child: HuaweiMap(
                  initialCameraPosition: CameraPosition(
                    target: _center,
                    zoom: _zoom,
                  ),
                  onMapCreated: _onMapCreated, // Added Line
                  mapType: MapType.normal,
                  tiltGesturesEnabled: true,

/*
  Rest of the code
*/

进入全屏模式 退出全屏模式

我们现在有一个用于非用户相机移动的控制器,所以让我们使用控制器。我用 InkWell 小部件包装了 LocationCards 以提供 onTap 功能。插件 CameraUpdate 类中有几个有用的方法使我们能够放大、缩小或更改相机位置。我们将使用 newLatLngZoom 方法来放大指定位置,然后通过使用控制器和 animateCamera 方法,我们将动画相机移动到我们的新相机位置。您可以使用 CameraUpdate 和控制器找到包装好的 LocationCard。

InkWell(
     onTap: () {
       CameraUpdate cameraUpdate =
           CameraUpdate.newLatLngZoom(
               _location1, _zoomMarker);
       mapController.animateCamera(cameraUpdate);
     },
     child: LocationCard(
       title: "Location 1",
       motto: "A Fine Dining Restaurant",
       address:
           "Avrupa Yakası, Cihangir, 34433 Beyoğlu/İstanbul Türkiye",
     )),

进入全屏模式 退出全屏模式

使用的 _zoomMarker 变量是一个双精度常量,值为 18。此外,使用的 _location1 变量是我们在创建标记时设置的变量。

完成这些步骤后,点击一张卡片,您将在 HuaweiMap 小部件中看到随着缩放级别的变化而平滑的相机移动动画。瞧! 🎉

[Alt](https://res.cloudinary.com/practicaldev/image/fetch/s--PtfmhvaM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev- to-uploads.s3.amazonaws.com/i/udcqwqleu6kc77cc81c.gif)

正如我之前提到的,您还可以设置一些类似于标记的圆、折线或多边形。此外,您可以将一些点击操作添加到您的地图和您设置的形状或标记中。不要忘记探索 Huawei Map Kit Flutter Plugin 提供的其他功能。

如果您想自己检查或尝试此示例,我将留下项目的 GitHub 链接。您还可以从链接中找到 LocationCard 的代码和我所做的其他细微调整。

https://github.com/SerdarCanDev/FlutterHuaweiMapTutorial

结论

由于华为创建了自己的服务,对 Flutter、React Native、Cordova 或 Xamarin 等跨平台框架的支持需求增加。为了满足这些需求,华为不断发布插件和更新以支持其开发人员。我们已经在 Flutter 项目中学习了如何使用华为的 Map Kit,但还有更多官方插件可供华为服务探索。

为了进一步阅读,我将在“参考和进一步阅读”部分提供一些链接,其中包括展示另一项服务的文章。您也可以在评论部分提出与本文相关的任何问题。

https://developer.huawei.com/consumer/en/hms/huawei-MapKit

https://pub.dev/publishers/developer.huawei.com/packages

使用华为 Push Kit 插件在 Flutter 上发送推送通知:

https://medium.com/huawei-developers/sending-push-notifications-on-flutter-with-huawei-push-kit-plugin-534787862b4d

欲知更多详情,您可以前往:

l 官网:https://developer.huawei.com/consumer/en/hms

l 文档页面:https://developer.huawei.com/consumer/en/doc/development

l Reddit 加入我们的开发者讨论:https://www.reddit.com/r/HMSCore/

l GitHub:https://github.com/HMS-Core

l Stack Overflow:https://stackoverflow.com/questions/tagged/huawei-mobile-services

Logo

ModelScope旨在打造下一代开源的模型即服务共享平台,为泛AI开发者提供灵活、易用、低成本的一站式模型服务产品,让模型应用更简单!

更多推荐