1. 插件介绍

Flutter跨平台开发鸿蒙化定位服务组件是一个专为OpenHarmony系统设计的Flutter插件,它实现了Flutter与OpenHarmony原生定位服务的无缝集成。该组件基于OpenHarmony的LocationKit提供高精度定位功能,通过MethodChannel实现Flutter与原生鸿蒙代码的双向通信,支持实时获取设备位置信息并在Flutter界面中展示。

主要功能特性:

  • 基于OpenHarmony LocationKit实现高精度定位
  • 通过MethodChannel实现跨平台通信
  • 支持自定义定位请求参数(优先级、时间间隔、距离间隔等)
  • 集成fluttertoast实现位置信息的弹窗提示
  • 完整的错误处理机制

2. 环境设置

2.1 系统要求

  • Flutter SDK: ≥ 2.19.6
  • Dart SDK: ≥ 2.19.6 < 3.0.0
  • OpenHarmony SDK: API 9及以上
  • DevEco Studio: 4.0及以上

2.2 项目配置

  1. 在Flutter项目中创建OpenHarmony模块
flutter create -t module your_project_name
  1. 进入OpenHarmony模块目录
cd your_project_name/ohos
  1. 配置模块信息(module.json5),添加定位权限
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "Flutter OpenHarmony Location Demo",
    "mainElement": "EntryAbility",
    "deviceTypes": ["phone", "tablet"],
    "distro": {
      "deliveryWithInstall": true,
      "moduleName": "entry",
      "moduleType": "entry"
    },
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntrance": "./ets/entryability/EntryAbility.ets",
        "description": "",
        "icon": "$media:icon",
        "label": "$string:app_name",
        "type": "page",
        "visible": true,
        "skills": [
          {
            "entities": ["entity.system.home"],
            "actions": ["action.system.home"]
          }
        ]
      }
    ],
    "requestPermissions": [
      {
        "name": "ohos.permission.LOCATION"
      },
      {
        "name": "ohos.permission.LOCATION_IN_BACKGROUND"
      }
    ]
  }
}

3. 包的引入

由于本定位服务组件依赖于自定义修改的fluttertoast包,需要通过AtomGit方式引入。在Flutter项目的pubspec.yaml文件中添加以下配置:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.2
  fluttertoast:
    git:
      url: "https://atomgit.com/openharmony-sig/flutter_fluttertoast.git"
      path: "fluttertoast"

然后执行flutter pub get命令获取依赖:

flutter pub get

4. API的调用

4.1 MethodChannel通信配置

在Flutter项目的lib/main.dart中配置MethodChannel,用于接收来自OpenHarmony原生代码的定位信息:

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('获取定位信息'),
        ),
        body: const MessageReceiver(),
      ),
    );
  }
}

class MessageReceiver extends StatefulWidget {
  const MessageReceiver({super.key});

  
  _MessageReceiverState createState() => _MessageReceiverState();
}

class _MessageReceiverState extends State<MessageReceiver> {
  String? message;

  
  void initState() {
    super.initState();
    _setupMethodChannel();
  }

  Future<void> _setupMethodChannel() async {
    const channel = MethodChannel('com.example.yourapp/channel');
    channel.setMethodCallHandler((call) async {
      if (call.method == 'sendMessage') {
        final String? msg = call.arguments as String?;
        setState(() {
          message = msg;
        });
        Fluttertoast.showToast(
          msg: msg.toString(),
          toastLength: Toast.LENGTH_SHORT,
          backgroundColor: Colors.red,
          textColor: Colors.white
        );
      }
    });
  }

  
  Widget build(BuildContext context) {
    return Center(
      child: message == null
          ? Text('暂无定位信息')
          : Text('来自ArkTS的定位信息: $message'),
    );
  }
}

4.2 OpenHarmony原生定位服务实现

在OpenHarmony模块中实现定位服务,首先创建自定义插件CustomPlugin.ets

import MethodChannel, {
  MethodCallHandler,
  MethodResult
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import common from '@ohos.app.ability.common';
import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger';
import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
import {
  FlutterPlugin,
  FlutterPluginBinding
} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';

export default class CustomPlugin implements FlutterPlugin, MethodCallHandler {
  private context: common.Context | null = null;
  private channel: MethodChannel | null = null;

  private static instance: CustomPlugin | null = null;
  public static getInstance(): CustomPlugin {
    if (!CustomPlugin.instance) {
      CustomPlugin.instance = new CustomPlugin();
    }
    return CustomPlugin.instance;
  }
  constructor() {
    if (!CustomPlugin.instance) {
      CustomPlugin.instance = this;
    }else {
      return CustomPlugin.instance
    }
  }

  getUniqueClassName(): string {
    return "CustomPlugin";
  }

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.context = binding.getApplicationContext();
    this.channel = new MethodChannel(binding.getBinaryMessenger(), 'com.example.yourapp/channel');
    this.channel.setMethodCallHandler(this);
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
    throw new Error('Method not implemented.');
  }

  public sendMessage(res: String): void {
    this.channel?.invokeMethod('sendMessage', res);
  }
}

4.3 注册自定义插件

EntryAbility.ets中注册自定义插件:

import { FlutterAbility } from '@ohos/flutter_ohos'
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import FlutterEngine from '@ohos/flutter_ohos/src/main/ets/embedding/engine/FlutterEngine';
import CustomPlugin from './CustomPlugin';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this.addPlugin(new CustomPlugin());
  }
}

4.4 实现定位功能

在OpenHarmony页面中实现定位功能,创建Index.ets

import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'
import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import CustomPlugin from '../entryability/CustomPlugin';

// 获取当前位置信息
function getCurrentLocationInfo() {
  const requestInfo: geoLocationManager.LocationRequest = {
    'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX,
    'scenario': geoLocationManager.LocationRequestScenario.UNSET,
    'timeInterval': 1,
    'distanceInterval': 0,
    'maxAccuracy': 0
  };
  const custom = CustomPlugin.getInstance()
  try {
    geoLocationManager.getCurrentLocation(requestInfo)
      .then((location: geoLocationManager.Location) => {
        let value: string = JSON.stringify(location);
        custom.sendMessage(value);
      })
      .catch((err: BusinessError) => {
        console.error(`Failed to get current location. Code is ${err.code}, message is ${err.message}`);
      });
  } catch (err) {
    console.error(`Failed to get current location. Code is ${err.code}, message is ${err.message}`);
  }
}

let storage = LocalStorage.getShared();
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'

@Entry(storage)
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext
  @LocalStorageLink('viewId') viewId: string = "";

  build() {
    Column() {
      FlutterPage({ viewId: this.viewId });
      Button('获取定位信息')
        .onClick((event)=>{
          getCurrentLocationInfo();
        })
        .position({ x: '35%', y: '20%' })
        .backgroundColor('#007AFF')
        .fontColor('#FFFFFF')
        .padding({ left: 20, right: 20, top: 10, bottom: 10 })
        .borderRadius(10)
    }
  }

  onBackPress(): boolean {
    this.context.eventHub.emit(EVENT_BACK_PRESS)
    return true
  }
}

5. 完整示例

5.1 Flutter端完整代码

// lib/main.dart
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';

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

class MyApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('获取定位信息'),
        ),
        body: const MessageReceiver(),
      ),
    );
  }
}

class MessageReceiver extends StatefulWidget {
  const MessageReceiver({super.key});

  
  _MessageReceiverState createState() => _MessageReceiverState();
}

class _MessageReceiverState extends State<MessageReceiver> {
  String? message;

  
  void initState() {
    super.initState();
    _setupMethodChannel();
  }

  Future<void> _setupMethodChannel() async {
    const channel = MethodChannel('com.example.yourapp/channel');
    channel.setMethodCallHandler((call) async {
      if (call.method == 'sendMessage') {
        final String? msg = call.arguments as String?;
        setState(() {
          message = msg;
        });
        Fluttertoast.showToast(
          msg: msg.toString(),
          toastLength: Toast.LENGTH_SHORT,
          backgroundColor: Colors.red,
          textColor: Colors.white
        );
      }
    });
  }

  
  Widget build(BuildContext context) {
    return Center(
      child: message == null
          ? Text('暂无定位信息')
          : Text('来自ArkTS的定位信息: $message'),
    );
  }
}

5.2 OpenHarmony端完整代码

// ets/entryability/CustomPlugin.ets
import MethodChannel, {
  MethodCallHandler,
  MethodResult
} from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import common from '@ohos.app.ability.common';
import { BinaryMessenger } from '@ohos/flutter_ohos/src/main/ets/plugin/common/BinaryMessenger';
import MethodCall from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodCall';
import {
  FlutterPlugin,
  FlutterPluginBinding
} from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';

export default class CustomPlugin implements FlutterPlugin, MethodCallHandler {
  private context: common.Context | null = null;
  private channel: MethodChannel | null = null;

  private static instance: CustomPlugin | null = null;
  public static getInstance(): CustomPlugin {
    if (!CustomPlugin.instance) {
      CustomPlugin.instance = new CustomPlugin();
    }
    return CustomPlugin.instance;
  }
  constructor() {
    if (!CustomPlugin.instance) {
      CustomPlugin.instance = this;
    }else {
      return CustomPlugin.instance
    }
  }

  getUniqueClassName(): string {
    return "CustomPlugin";
  }

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.context = binding.getApplicationContext();
    this.channel = new MethodChannel(binding.getBinaryMessenger(), 'com.example.yourapp/channel');
    this.channel.setMethodCallHandler(this);
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
  }

  onMethodCall(call: MethodCall, result: MethodResult): void {
    throw new Error('Method not implemented.');
  }

  public sendMessage(res: String): void {
    this.channel?.invokeMethod('sendMessage', res);
  }
}
// ets/entryability/EntryAbility.ets
import { FlutterAbility } from '@ohos/flutter_ohos'
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import FlutterEngine from '@ohos/flutter_ohos/src/main/ets/embedding/engine/FlutterEngine';
import CustomPlugin from './CustomPlugin';

export default class EntryAbility extends FlutterAbility {
  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this.addPlugin(new CustomPlugin());
  }
}
// ets/pages/Index.ets
import common from '@ohos.app.ability.common';
import { FlutterPage } from '@ohos/flutter_ohos'
import { geoLocationManager } from '@kit.LocationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import CustomPlugin from '../entryability/CustomPlugin';

// 获取当前位置信息
function getCurrentLocationInfo() {
  const requestInfo: geoLocationManager.LocationRequest = {
    'priority': geoLocationManager.LocationRequestPriority.FIRST_FIX,
    'scenario': geoLocationManager.LocationRequestScenario.UNSET,
    'timeInterval': 1,
    'distanceInterval': 0,
    'maxAccuracy': 0
  };
  const custom = CustomPlugin.getInstance()
  try {
    geoLocationManager.getCurrentLocation(requestInfo)
      .then((location: geoLocationManager.Location) => {
        let value: string = JSON.stringify(location);
        custom.sendMessage(value);
      })
      .catch((err: BusinessError) => {
        console.error(`Failed to get current location. Code is ${err.code}, message is ${err.message}`);
      });
  } catch (err) {
    console.error(`Failed to get current location. Code is ${err.code}, message is ${err.message}`);
  }
}

let storage = LocalStorage.getShared();
const EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'

@Entry(storage)
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext
  @LocalStorageLink('viewId') viewId: string = "";

  build() {
    Column() {
      FlutterPage({ viewId: this.viewId });
      Button('获取定位信息')
        .onClick((event)=>{
          getCurrentLocationInfo();
        })
        .position({ x: '35%', y: '20%' })
        .backgroundColor('#007AFF')
        .fontColor('#FFFFFF')
        .padding({ left: 20, right: 20, top: 10, bottom: 10 })
        .borderRadius(10)
    }
  }

  onBackPress(): boolean {
    this.context.eventHub.emit(EVENT_BACK_PRESS)
    return true
  }
}

6. 运行与测试

6.1 运行步骤

  1. 连接OpenHarmony设备或启动模拟器
  2. 进入Flutter项目根目录
  3. 执行以下命令构建并运行项目
flutter run -d ohos

6.2 测试方法

  1. 首次运行时,应用会请求定位权限,点击"允许"
  2. 点击页面上的"获取定位信息"按钮
  3. 应用会调用OpenHarmony的定位服务获取当前位置
  4. 获取到的位置信息会通过MethodChannel发送到Flutter端
  5. Flutter端会在页面上显示位置信息,并通过toast弹窗提示

7. 总结

本指南介绍了如何在OpenHarmony系统上使用Flutter跨平台开发鸿蒙化定位服务组件。通过本组件,开发者可以轻松实现Flutter与OpenHarmony原生定位服务的集成,获取设备的位置信息并在Flutter界面中展示。

主要实现了以下功能:

  • 使用OpenHarmony LocationKit获取高精度定位信息
  • 通过MethodChannel实现Flutter与OpenHarmony的跨平台通信
  • 集成fluttertoast实现位置信息的弹窗提示
  • 完整的权限配置和错误处理机制

通过本组件的学习,开发者可以掌握Flutter与OpenHarmony原生服务集成的核心技术,为开发更多跨平台应用打下基础。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐