Skip to content

Commit

Permalink
feat: android platform view host modes (#56)
Browse files Browse the repository at this point in the history
- [x] public API
- [x] test texture layer
- [x] test hybrid composition


https://docs.flutter.dev/platform-integration/android/platform-views#hybrid-composition-1
  • Loading branch information
josxha authored Nov 2, 2024
1 parent 164a856 commit 8b675cf
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.github.josxha.maplibre

// if imports can't resolve: https://stackoverflow.com/a/65903576/9439899

import CameraChangeReason
import LngLat
import MapCamera
Expand Down
37 changes: 2 additions & 35 deletions docs/docs/getting-started/setup-android.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,37 +37,6 @@ buildscript {
}
```

## Adjust the minSdk version of Android

The `maplibre` package on Flutter
uses [platform views](https://docs.flutter.dev/platform-integration/android/platform-views)
to display the native map. This requires an Android SDK version of at least 23
(Android 6.0).

:::tip

The app compiles with a minSdk version of down to 21. However, the MapLibre map
won't be visible on these Android versions. You can check for the SDK version
with [device_info_plus](https://pub.dev/packages/device_info_plus)
and render an alternative map as a workaround or limit the supported Android
versions to Android 6.0 and onwards like demonstrated here.

:::

Open your `android/app/build.gradle` file and ensure that it is set to 23 or
higher.

```gradle title="android/app/build.gradle"
android {
defaultConfig {
// ...
// highlight-next-line
minSdk = 23 // previously flutter.minSdkVersion
// ...
}
}
```

## Set the permissions

If you want to show the user's location on the map you need to add
Expand All @@ -86,7 +55,5 @@ manifest`android/app/src/main/AndroidManifest.xml`.
</manifest>
```

Starting from Android API level 23 you also need to request it at runtime. This
plugin does not handle this for you. You can either use the flutter package
[location](https://pub.dev/packages/location)
or [permission_handler](https://pub.dev/packages/permission_handler) for this.
Starting from Android API level 23 you also need to request it at runtime.
You can use the `PermissionManager` for it.
4 changes: 2 additions & 2 deletions example/android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ android {

defaultConfig {
applicationId = "com.github.josxha.maplibre_example"
// minSdk 21 required by maplibre, 23 required by Flutter platform views
minSdk = 23 // flutter.minSdkVersion
// minSdk 21 required by maplibre
minSdk = 21 // flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
Expand Down
1 change: 1 addition & 0 deletions lib/maplibre.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// the dependency themselves.
export 'package:geotypes/geotypes.dart';

export 'src/android_platform_view_mode.dart';
export 'src/annotation/annotation_layer.dart';
export 'src/lng_lat_bounds.dart';
export 'src/map.dart';
Expand Down
23 changes: 23 additions & 0 deletions lib/src/android_platform_view_mode.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// ignore_for_file: constant_identifier_names

/// The different ways a MapLibre map can be displayed on Android.
///
/// https://docs.flutter.dev/platform-integration/android/platform-views
/// https://github.com/flutter/flutter/blob/master/docs/platforms/android/Android-Platform-Views.md
enum AndroidPlatformViewMode {
/// [Texture Layer Hybrid Composition](https://github.com/flutter/flutter/blob/master/docs/platforms/android/Texture-Layer-Hybrid-Composition.md)
/// with fallback to Virtual Display, when the current SDK version is <23
/// or MapWidget.textureView is false.
tlhc_vd,

/// [Texture Layer Hybrid Composition](https://github.com/flutter/flutter/blob/master/docs/platforms/android/Texture-Layer-Hybrid-Composition.md)
/// with fallback to Hybrid Composition, when the current SDK version is <23
/// or MapWidget.textureView is false.
tlhc_hc,

/// Always use [Hybrid Composition](https://github.com/flutter/flutter/blob/master/docs/platforms/Hybrid-Composition.md).
hc,

/// Always use [Virtual Display](https://github.com/flutter/flutter/blob/master/docs/platforms/android/Virtual-Display.md).
vd;
}
6 changes: 6 additions & 0 deletions lib/src/map_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class MapOptions {
this.nativeLogo = true,
this.attribution = true,
this.androidTextureMode = true,
this.androidMode = AndroidPlatformViewMode.tlhc_vd,
}) : initPitch = pitch ?? initPitch;

/// The style URL that should get used. If not set, the default MapLibre style
Expand Down Expand Up @@ -81,6 +82,11 @@ class MapOptions {
/// Toggle the MapLibre Native logo.
final bool attribution;

/// The platform view type used on android.
///
/// https://docs.flutter.dev/platform-integration/android/platform-views
final AndroidPlatformViewMode androidMode;

/// Toggle the texture mode on Android.
///
/// textureMode comes at a significant performance penalty.
Expand Down
84 changes: 66 additions & 18 deletions lib/src/native/widget_state_jni.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import 'dart:async';
import 'dart:ui';

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' hide Layer;
import 'package:flutter/services.dart';
import 'package:jni/jni.dart';
import 'package:maplibre/maplibre.dart';
Expand Down Expand Up @@ -43,24 +46,69 @@ final class MapLibreMapStateJni extends MapLibreMapState

@override
Widget buildPlatformWidget(BuildContext context) {
// Texture Layer (or Texture Layer Hybrid Composition)
// Platform Views are rendered into a texture. Flutter draws the
// platform views (via the texture). Flutter content is rendered
// directly into a Surface.
// + good performance for Android Views
// + best performance for Flutter rendering.
// + all transformations work correctly.
// - quick scrolling (e.g. a web view) will be janky
// - SurfaceViews are problematic in this mode and will be moved into a
// virtual display (breaking a11y)
// - Text magnifier will break unless Flutter is rendered into a
// TextureView.
// https://docs.flutter.dev/platform-integration/android/platform-views#texturelayerhybridcompisition
return AndroidView(
viewType: 'plugins.flutter.io/maplibre',
onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers,
creationParamsCodec: const StandardMessageCodec(),
const viewType = 'plugins.flutter.io/maplibre';
final mode = _options.androidMode;
if (mode == AndroidPlatformViewMode.tlhc_vd) {
return AndroidView(
viewType: viewType,
onPlatformViewCreated: _onPlatformViewCreated,
gestureRecognizers: widget.gestureRecognizers,
);
}
return PlatformViewLink(
viewType: viewType,
surfaceFactory: (context, controller) {
return AndroidViewSurface(
controller: controller as AndroidViewController,
gestureRecognizers: widget.gestureRecognizers ??
const <Factory<OneSequenceGestureRecognizer>>{},
hitTestBehavior: PlatformViewHitTestBehavior.opaque,
);
},
onCreatePlatformView: (params) {
final viewController = switch (mode) {
// This attempts to use the newest and most efficient platform view
// implementation when possible. In cases where that is not
// supported, it falls back to using Hybrid Composition, which is
// the mode used by initExpensiveAndroidView.
// https://api.flutter.dev/flutter/services/PlatformViewsService/initSurfaceAndroidView.html
// https://github.com/flutter/flutter/blob/master/docs/platforms/android/Android-Platform-Views.md#selecting-a-mode
AndroidPlatformViewMode.tlhc_hc =>
PlatformViewsService.initSurfaceAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
onFocus: () => params.onFocusChanged(true),
),
AndroidPlatformViewMode.tlhc_vd =>
PlatformViewsService.initAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
onFocus: () => params.onFocusChanged(true),
),
AndroidPlatformViewMode.hc =>
PlatformViewsService.initExpensiveAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
onFocus: () => params.onFocusChanged(true),
),
// https://github.com/flutter/flutter/blob/master/docs/platforms/android/Virtual-Display.md
AndroidPlatformViewMode.vd => PlatformViewsService.initAndroidView(
id: params.id,
viewType: viewType,
layoutDirection: TextDirection.ltr,
onFocus: () => params.onFocusChanged(true),
),
};
return viewController
..addOnPlatformViewCreatedListener((id) {
params.onPlatformViewCreated(id);
_onPlatformViewCreated(id);
})
..create();
},
);
}

Expand Down

0 comments on commit 8b675cf

Please sign in to comment.