Mobile Development 19 min read

Flutter Amap Integration in OpenHarmony: Architecture, Optimization & HarmonyOS Bridge Implementation

This article details the integration and optimization of Amap Flutter plugins (amap_flutter_map, amap_flutter_location) in an OpenHarmony project, covering Clean Architecture layering, tile caching, memory management, cross-platform adaptations for Android/iOS, error handling, and a complete HarmonyOS bridge/adapter implementation in ArkTS.

51CTO HarmonyOS Developer Community
51CTO HarmonyOS Developer Community
51CTO HarmonyOS Developer Community
Flutter Amap Integration in OpenHarmony: Architecture, Optimization & HarmonyOS Bridge Implementation

Project Background and Challenges

In the Yunpu Flutter project, map functionality handles store location, delivery route planning, and user tracking. Initial integration of Amap Maps faced four key challenges:

Cross-platform consistency : Android and iOS differ in map rendering and gesture interaction.

Performance optimization : Map components consume significant memory and CPU.

Permission management : Location permission requests and state management require robust flow design.

Offline support : Ensuring basic map functionality under unstable network conditions.

Architecture Design and Integration Strategy

1. Layered Architecture (Clean Architecture)

The map feature is split into three layers:

Presentation Layer : UI rendering and user interaction.

Business Logic Layer : Map-related business rules.

Data Layer : Encapsulates raw Amap SDK interfaces.

This decouples map functionality from business logic, easing testing and maintenance.

2. Plugin Initialization and Configuration

Lazy initialization avoids unnecessary overhead at app start:

class MapService {
  static final MapService _instance = MapService._internal();
  factory MapService() => _instance;
  MapService._internal() {
    _initAMap();
  }
  Future<void> _initAMap() async {
    await AMapFlutterLocation.setApiKey(
      androidKey: 'your_android_key',
      iosKey: 'your_ios_key'
    );
  }
}

3. Location Service Encapsulation

A unified LocationManager handles permission checks, configuration, and streaming updates:

class LocationManager {
  final AMapFlutterLocation _locationPlugin = AMapFlutterLocation();
  final StreamController<LocationResult> _locationController = 
      StreamController<LocationResult>.broadcast();
  Stream<LocationResult> get locationStream => _locationController.stream;

  Future<void> startLocation() async {
    final status = await Permission.location.request();
    if (status.isGranted) {
      final locationOption = LocationOption(
        needAddress: true,
        geoLanguage: GeoLanguage.ZH,
      );
      _locationPlugin.setLocationOption(locationOption);
      _locationPlugin.onLocationChanged().listen((location) {
        _handleLocationUpdate(location);
      });
      _locationPlugin.startLocation();
    }
  }

  void _handleLocationUpdate(Map<String, Object> location) {
    final result = LocationResult.fromMap(location);
    _locationController.add(result);
  }
}

Performance Optimization Practices

1. Map Tile Caching Strategy

A custom TileCacheManager uses a two-level cache (memory LRU + local file) with a max of 100 tiles:

class TileCacheManager {
  static const int _maxCacheSize = 100;
  static final LinkedHashMap<String, Uint8List> _tileCache = 
      LinkedHashMap<String, Uint8List>();

  static Future<Uint8List?> getTile(int x, int y, int z) async {
    final key = '$x-$y-$z';
    if (_tileCache.containsKey(key)) {
      return _tileCache[key];
    }
    final file = await _getCacheFile(key);
    if (await file.exists()) {
      final data = await file.readAsBytes();
      _updateCache(key, data);
      return data;
    }
    final tileData = await _downloadTile(x, y, z);
    if (tileData != null) {
      await _saveToCache(key, tileData);
      _updateCache(key, tileData);
    }
    return tileData;
  }

  static void _updateCache(String key, Uint8List data) {
    if (_tileCache.length >= _maxCacheSize) {
      final firstKey = _tileCache.keys.first;
      _tileCache.remove(firstKey);
    }
    _tileCache[key] = data;
  }
}

2. Memory Management

Timely resource release : Actively dispose map resources on page destruction.

Listener management : Centralize location listeners to avoid duplicate registration.

Image resource optimization : Compress and cache map marker icons.

Cross-Platform Adaptation Experience

1. Android Optimizations

Permission flow adapted for Android 11 changes.

Foreground service used for background location to maintain accuracy.

Battery-friendly location update strategies.

2. iOS Adaptations

Complete privacy descriptions for location usage.

Proper background location mode configuration.

Well-timed permission request prompts.

Error Handling and Monitoring

1. Exception Capture Mechanism

MapErrorHandler

classifies platform exceptions and triggers UI responses:

class MapErrorHandler {
  static void handleLocationError(dynamic error) {
    if (error is PlatformException) {
      switch (error.code) {
        case 'PERMISSION_DENIED':
          _showPermissionDialog();
          break;
        case 'SERVICE_UNAVAILABLE':
          _showServiceUnavailable();
          break;
        case 'LOCATION_TIMEOUT':
          _retryLocation();
          break;
      }
    }
    _reportError(error);
  }
}

2. Performance Monitoring

Real-time tracking of map load time, location accuracy changes, memory usage, and battery consumption.

Practical Case: Delivery Route Planning

Complete route planning with waypoints and fastest strategy:

class DeliveryRoutePlanner {
  Future<RouteResult> planRoute(
    LatLng start,
    LatLng end,
    List<LatLng> waypoints
  ) async {
    try {
      final request = RoutePlanningRequest(
        origin: start,
        destination: end,
        waypoints: waypoints,
        strategy: RouteStrategy.FASTEST
      );
      final response = await AMapRoutePlanning.calculateRoute(request);
      if (response.status == RouteStatus.SUCCESS) {
        return RouteResult.success(response.paths.first);
      } else {
        return RouteResult.failure(response.error);
      }
    } catch (e) {
      return RouteResult.failure('路径规划失败: $e');
    }
  }
}

HarmonyOS Map Implementation

To support OpenHarmony, a HarmonyOS bridge and adapter provide the same API surface as the Flutter plugins.

1. HarmonyMapBridge (ETS/ArkTS)

Singleton bridge using @ohos.geoLocationManager and @ohos.data.preferences for persistence. Key methods: initialize(): Sets up data storage, location service, and default map config (zoom 15, buildings on, rotate/zoom enabled). startLocation(): Checks permission, configures LocationRequest (priority FIRST_FIX, scenario NAVIGATION, maxAccuracy 10m, interval 1s), registers locationChange callback. stopLocation(): Unregisters callback. getCurrentLocation(): Returns last known location. setMapCenter(lat, lng): Persists center coordinates to preferences. addMarker(id, lat, lng, title): Stores marker list in preferences. dispose(): Stops location, clears preferences, resets state.

Location updates are saved to last_location preference and logged via hilog.

2. HarmonyMapAdapter (ETS/ArkTS)

Thin wrapper over the bridge exposing a Flutter-compatible API. Adds: calculateDistance(lat1, lng1, lat2, lng2): Haversine formula (Earth radius 6371 km). isLocationServiceAvailable(): Calls geoLocationManager.isLocationEnabled(). getMapConfig(): Returns default config (zoom 15, traffic off, buildings on).

All methods guard with isInitialized checks and log via hilog.

Key Functional Points

Location service integration : Precise positioning via HarmonyOS location service.

Map configuration management : Center point, zoom level, etc.

Marker management : Add and manage map markers.

Distance calculation : Integrated geodesic distance computation.

Permission management : Complete location permission check and request flow.

Considerations

Location permissions must be correctly configured in HarmonyOS.

Map data must stay synchronized with Amap services.

Offline maps require additional storage space management.

Summary and Outlook

Deep integration of Amap Flutter plugins yielded stable map features and valuable cross-platform experience. Key takeaways:

Architecture matters : Good architecture underpins functional stability.

Optimization is continuous : Requires ongoing tuning.

UX details count : Map interaction smoothness directly affects satisfaction.

Future plans include deeper integration with HarmonyOS distributed capabilities, enhanced offline maps, and AR navigation features.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

fluttercross-platformperformance-optimizationclean-architectureArkTSmap-integrationamapopenharmony
51CTO HarmonyOS Developer Community
Written by

51CTO HarmonyOS Developer Community

The HarmonyOS Developer Community is a learning-oriented community for developers to learn, communicate, ask questions, and share.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.